Unwrapping with `errors.As`
Part of the Logic & Flow section of Coddy's GO journey — lesson 41 of 68.
While errors.Is helps you identify specific error values in a chain, sometimes you need to access the actual fields and data of a custom error type. The errors.As function provides this capability by finding the first error in a chain that matches a specific type and extracting it for inspection.
The errors.As function takes two parameters: the error to examine and a pointer to a variable of the target error type. If it finds a matching error type in the chain, it sets your variable to that error and returns true:
var validationErr ValidationError
if errors.As(wrappedErr, &validationErr) {
fmt.Printf("Field: %s, Message: %s\n", validationErr.Field, validationErr.Message)
}This is particularly powerful when working with custom error types that contain additional context. Unlike errors.Is which only checks for equality, errors.As extracts the actual error instance, allowing you to access its fields and methods. This makes error handling more sophisticated and informative in complex applications.
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
Challenge
EasyBuild an API error handling system that uses errors.As to extract and examine custom error types from wrapped error chains. This challenge demonstrates how to use errors.As to access the fields and data of specific custom error types, even when they've been wrapped with additional context.
You will receive two inputs:
- A string containing API request details in the format
"endpoint,method,error_code,error_field"(e.g.,"/users,POST,400,email") - A string containing system context in the format
"service_name,request_id"(e.g.,"UserAPI,req_12345")
Your task is to:
- Create a custom error type called
APIErrorstruct with three fields:Code(int) - the HTTP status codeField(string) - the field that caused the errorMessage(string) - a description of the error
- Implement the
Error() stringmethod forAPIErrorthat returns:"API error [code]: [message] (field: [field])" - Parse the first input by splitting on commas to get endpoint, method, error code, and error field
- Parse the second input by splitting on commas to get service name and request ID
- Convert the error code string to an integer
- Create an
APIErrorinstance based on the error code:- If code is 400: message
"invalid request data" - If code is 401: message
"authentication required" - If code is 404: message
"resource not found" - If code is 500: message
"internal server error" - For any other code: message
"unknown error"
- If code is 400: message
- Create an endpoint-level wrapped error using
fmt.Errorfwith%w:"[method] [endpoint] failed: %w" - Create a service-level wrapped error:
"[service_name] request [request_id] failed: %w" - Use
errors.Asto extract theAPIErrorfrom the wrapped error chain and display the extraction result:- If extraction succeeds:
"API error extracted successfully" - If extraction fails:
"Failed to extract API error"
- If extraction succeeds:
- If the extraction succeeded, display the extracted error details:
"Extracted Error Details:""Code: [code]""Field: [field]""Message: [message]""Full error: [full_error_message_from_Error_method]"
- Display the complete error chain:
"Error Chain:""Original: [original_api_error_message]""Endpoint level: [endpoint_level_error_message]""Service level: [service_level_error_message]"
- Display a final analysis:
"Error Analysis:""Service: [service_name]""Request ID: [request_id]""Endpoint: [method] [endpoint]""HTTP Status: [code]""Problem Field: [field]""Error Category: [error_category]"where error_category is "Client Error" for codes 400-499, "Server Error" for codes 500-599, or "Unknown" for other codes
Use the strings package to split the input strings on commas, the strconv package to convert the error code string to an integer, the errors package for errors.As, and the fmt package for error wrapping. This challenge demonstrates how errors.As allows you to access the specific fields and methods of custom error types within wrapped error chains, enabling sophisticated error handling and analysis.
Cheat sheet
The errors.As function extracts specific error types from error chains, allowing access to custom error fields and methods.
errors.As takes two parameters: the error to examine and a pointer to a variable of the target error type:
var validationErr ValidationError
if errors.As(wrappedErr, &validationErr) {
fmt.Printf("Field: %s, Message: %s\n", validationErr.Field, validationErr.Message)
}Unlike errors.Is which only checks for equality, errors.As extracts the actual error instance, enabling access to its fields and methods for more sophisticated error handling.
Try it yourself
package main
import (
"errors"
"fmt"
"strconv"
"strings"
)
func main() {
// Read input
var apiDetails string
var systemContext string
fmt.Scanln(&apiDetails)
fmt.Scanln(&systemContext)
// Parse inputs
apiParts := strings.Split(apiDetails, ",")
endpoint := apiParts[0]
method := apiParts[1]
errorCodeStr := apiParts[2]
errorField := apiParts[3]
contextParts := strings.Split(systemContext, ",")
serviceName := contextParts[0]
requestID := contextParts[1]
// Convert error code to integer
errorCode, _ := strconv.Atoi(errorCodeStr)
// TODO: Write your code below
// 1. Create APIError struct with Code, Field, Message fields
// 2. Implement Error() method for APIError
// 3. Create APIError instance based on error code
// 4. Create wrapped errors (endpoint-level and service-level)
// 5. Use errors.As to extract APIError from wrapped error chain
// 6. Display extraction result and error details
// 7. Display error chain and final analysis
}This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Advanced Control Flow
Switch with `fallthrough`Breaking from Nested LoopsContinuing a Specific LoopThe `goto` StatementRecap - Advanced Loop Control4Project: Simple Task List
Project SetupAdding a Task7Error Handling In-Depth
Custom Error TypesWrapping Errors with `%w`Unwrapping with `errors.Is`Unwrapping with `errors.As`Understanding `panic`Using `recover`Recap - Safe Division2Structs and Methods
Defining Methods on StructsValue ReceiversPointer ReceiversChoosing ReceiversMethods vs FunctionsRecap - Struct Behavior5Maps In-Depth
Maps of StructsPointers as Map ValuesTesting for Nil MapsComparing MapsRecap - Word Frequency Counter3Interfaces (The Basics)
What is an Interface?Defining an InterfaceImplementing an InterfaceUsing Interface TypesEmpty InterfaceType AssertionsType SwitchRecap - Shapes and Behaviors