Menu
Coddy logo textTech

Wrapping Errors with `%w`

Part of the Logic & Flow section of Coddy's GO journey — lesson 39 of 68.

When errors occur in your application, they often need additional context as they travel up through different layers of your code. Go provides a powerful mechanism for this through error wrapping using the %w format verb with fmt.Errorf.

Error wrapping allows you to add contextual information to an existing error while preserving the original error underneath. This creates an error chain that maintains the full history of what went wrong:

originalErr := errors.New("file not found")
wrappedErr := fmt.Errorf("failed to load config: %w", originalErr)

The %w verb is special - it tells fmt.Errorf to wrap the error rather than just convert it to a string. This preserves the original error so it can be examined later using functions like errors.Is and errors.As.

This pattern is particularly valuable in larger applications where an error might pass through multiple functions, each adding its own context. For example, a database connection error might get wrapped with "failed to connect to database", then wrapped again with "unable to start server", creating a clear trail of what happened and where.

quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

challenge icon

Challenge

Easy

Build a file processing system that wraps errors with contextual information as they propagate through different layers of your application. This challenge demonstrates how to use the %w format verb with fmt.Errorf to create error chains that preserve the original error while adding meaningful context.

You will receive two inputs:

  • A string containing file operation details in the format "filename,operation,status" (e.g., "config.json,read,permission_denied")
  • A string containing system context in the format "service_name,component" (e.g., "UserService,ConfigLoader")

Your task is to:

  1. Parse the first input by splitting on commas to get the filename, operation, and status
  2. Parse the second input by splitting on commas to get the service name and component
  3. Create an original error based on the status using errors.New():
    • If status is "permission_denied": "permission denied"
    • If status is "not_found": "file not found"
    • If status is "corrupted": "file corrupted"
    • For any other status: "unknown error"
  4. Create a file-level wrapped error using fmt.Errorf with %w: "failed to [operation] file [filename]: %w"
  5. Create a component-level wrapped error: "[component] error: %w"
  6. Create a service-level wrapped error: "[service_name] operation failed: %w"
  7. Display the error chain by printing each level:
    • "Original error: [original_error_message]"
    • "File level: [file_level_error_message]"
    • "Component level: [component_level_error_message]"
    • "Service level: [service_level_error_message]"
  8. Display a summary of the error chain:
    • "Error Chain Summary:"
    • "File: [filename]"
    • "Operation: [operation]"
    • "Component: [component]"
    • "Service: [service_name]"
    • "Root cause: [original_error_message]"

Use the strings package to split the input strings on commas, the errors package to create the original error, and the fmt package to create wrapped errors. This challenge demonstrates how error wrapping with %w creates a clear trail of context as errors move through different layers of your application, making debugging much more effective.

Cheat sheet

Error wrapping allows you to add contextual information to an existing error while preserving the original error. Use the %w format verb with fmt.Errorf:

originalErr := errors.New("file not found")
wrappedErr := fmt.Errorf("failed to load config: %w", originalErr)

The %w verb preserves the original error so it can be examined later using functions like errors.Is and errors.As. This creates an error chain that maintains the full history of what went wrong, which is valuable when errors pass through multiple functions in larger applications.

Try it yourself

package main

import (
	"errors"
	"fmt"
	"strings"
)

func main() {
	// Read input
	var fileDetails string
	var systemContext string
	fmt.Scanln(&fileDetails)
	fmt.Scanln(&systemContext)
	
	// Parse inputs
	fileInfo := strings.Split(fileDetails, ",")
	filename := fileInfo[0]
	operation := fileInfo[1]
	status := fileInfo[2]
	
	contextInfo := strings.Split(systemContext, ",")
	serviceName := contextInfo[0]
	component := contextInfo[1]
	
	// TODO: Write your code below
	// Create original error based on status
	// Create file-level wrapped error
	// Create component-level wrapped error
	// Create service-level wrapped error
	// Print each error level and summary
	
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Logic & Flow