errors

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2025 License: MIT Imports: 3 Imported by: 5

README

errors

Structured error handling library.

Overview

The errors library provides a foundational error handling system with:

  • Structured error codes - Consistent categorization across the platform
  • Error classification - Retryable vs permanent for intelligent retry logic
  • Context preservation - Rich metadata without exposing sensitive data
  • Error wrapping - Maintain error chains with standard library compatibility
  • JSON serialization - API-friendly error responses

Installation

go get github.com/jmgilman/go/errors

Quick Start

import "github.com/jmgilman/go/errors"

// Create an error
err := errors.New(errors.CodeNotFound, "user not found")

// Wrap an error
if err := db.Query(ctx, id); err != nil {
    return errors.Wrap(err, errors.CodeDatabase, "failed to query user")
}

// Add context
err = errors.WithContext(err, "user_id", id)
err = errors.WithContext(err, "operation", "login")

// Check if retryable
if errors.IsRetryable(err) {
    // Retry with backoff
}

// Serialize for API
response := errors.ToJSON(err)
json.NewEncoder(w).Encode(response)

Features

Error Codes

25 predefined error codes covering all platform scenarios:

  • Resource: CodeNotFound, CodeAlreadyExists, CodeConflict
  • Permission: CodeUnauthorized, CodeForbidden
  • Validation: CodeInvalidInput, CodeInvalidConfig, CodeSchemaFailed
  • Infrastructure: CodeDatabase, CodeNetwork, CodeTimeout, CodeRateLimit
  • Execution: CodeExecutionFailed, CodeBuildFailed, CodePublishFailed
  • CUE: CodeCUELoadFailed, CodeCUEBuildFailed, CodeCUEValidationFailed, CodeCUEDecodeFailed, CodeCUEEncodeFailed
  • Schema: CodeSchemaVersionIncompatible
  • System: CodeInternal, CodeNotImplemented, CodeUnavailable
  • Generic: CodeUnknown
Classification

Errors are automatically classified:

  • Retryable: Temporary failures (network, timeout, rate limit)
  • Permanent: Logic errors (validation, not found, permission denied)

Use errors.IsRetryable(err) for retry decisions.

Context Metadata

Attach debugging information to errors:

err = errors.WithContextMap(err, map[string]interface{}{
    "project": "api",
    "phase": "test",
    "duration": "2m30s",
})
Standard Library Compatibility

Works seamlessly with errors.Is, errors.As, and errors.Unwrap:

if errors.Is(err, sql.ErrNoRows) {
    // Handle no rows
}

var platformErr errors.PlatformError
if errors.As(err, &platformErr) {
    code := platformErr.Code()
}

Documentation

Full documentation: https://pkg.go.dev/github.com/jmgilman/go/errors

Performance

  • Error creation: <10μs
  • Error wrapping: <5μs
  • Context attachment: <2μs

See benchmarks for detailed performance characteristics.

License

Apache 2.0

Documentation

Overview

Package errors provides a foundational error handling system. It extends Go's standard error handling with structured error codes, retry classification, context preservation, and API serialization capabilities.

Package errors provides structured error handling.

This package extends Go's standard error handling with error codes, classification (retryable vs permanent), context metadata, and JSON serialization. It maintains full compatibility with the standard library errors package (errors.Is, errors.As, errors.Unwrap).

Features

  • Structured error codes for consistent categorization
  • Error classification for intelligent retry logic (retryable vs permanent)
  • Context metadata attachment for debugging
  • Error wrapping that preserves the error chain
  • JSON serialization for API responses
  • Zero dependencies (Layer 0 library)

Design Principles

  • Standard library compatibility (errors.Is, errors.As, errors.Unwrap)
  • Immutability (errors are immutable once created)
  • Type safety (strong types for codes and classifications)
  • Simplicity (minimal API surface, easy to use correctly)
  • Performance (optimized for error creation in hot paths)

Quick Start

Creating errors:

// Simple error
err := errors.New(errors.CodeNotFound, "user not found")

// Formatted error
err := errors.Newf(errors.CodeInvalidInput, "invalid age: %d", age)

Wrapping errors:

result, err := repo.Query(ctx, id)
if err != nil {
    return errors.Wrap(err, errors.CodeDatabase, "failed to query user")
}

Adding context:

err := errors.New(errors.CodeBuildFailed, "build failed")
err = errors.WithContext(err, "project", "api")
err = errors.WithContext(err, "phase", "test")

Retry logic:

if errors.IsRetryable(err) {
    // Implement retry with backoff
    time.Sleep(backoff)
    return retry(operation)
}

JSON serialization:

func handleError(w http.ResponseWriter, err error) {
    response := errors.ToJSON(err)
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(getHTTPStatus(errors.GetCode(err)))
    json.NewEncoder(w).Encode(response)
}

Error Codes

The library provides predefined error codes for all common platform scenarios:

  • Resource errors: CodeNotFound, CodeAlreadyExists, CodeConflict
  • Permission errors: CodeUnauthorized, CodeForbidden
  • Validation errors: CodeInvalidInput, CodeInvalidConfig, CodeSchemaFailed
  • Infrastructure errors: CodeDatabase, CodeNetwork, CodeTimeout, CodeRateLimit
  • Execution errors: CodeExecutionFailed, CodeBuildFailed, CodePublishFailed
  • System errors: CodeInternal, CodeNotImplemented, CodeUnavailable
  • Generic: CodeUnknown

Each error code has a default classification (retryable or permanent) that can be overridden when needed.

Error Classification

Errors are classified as either retryable or permanent:

  • Retryable: Temporary failures (network, timeout, rate limit, transient DB issues)
  • Permanent: Logic errors (validation, not found, permission denied)

Use errors.IsRetryable(err) to make retry decisions. The classification is preserved when wrapping errors and can be overridden with WithClassification.

Standard Library Compatibility

PlatformError implements the error interface and works seamlessly with standard library error functions:

// errors.Is traverses the error chain
if errors.Is(err, sql.ErrNoRows) {
    // Handle no rows
}

// errors.As finds typed errors in the chain
var platformErr errors.PlatformError
if errors.As(err, &platformErr) {
    code := platformErr.Code()
}

// errors.Unwrap retrieves the wrapped error
cause := errors.Unwrap(err)

Context Metadata

Attach debugging context to errors without exposing sensitive information:

err := errors.New(errors.CodeBuildFailed, "build failed")
err = errors.WithContextMap(err, map[string]interface{}{
    "project":    "api",
    "phase":      "test",
    "exit_code":  1,
    "duration":   "2m30s",
})

Context is included in JSON serialization but not in error chains exposed to external callers (security).

Best Practices

  • Always wrap errors with context: errors.Wrap(err, code, message)
  • Use specific error codes, not CodeUnknown
  • Don't include sensitive data in error messages or context
  • Use IsRetryable for retry decisions, not specific codes
  • Add context at each layer of the call stack
  • Preserve classification when wrapping (automatic)
  • Use ToJSON for API responses to prevent information leakage

Performance

The library is optimized for minimal overhead:

  • Error creation: <10μs
  • Error wrapping: <5μs
  • Context attachment: <2μs
  • Classification lookup: O(1)

See benchmarks for detailed performance characteristics.

Example (Workflow)

Example_workflow shows a complete error handling workflow across multiple layers.

package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	// Layer 1: Database layer
	dbErr := fmt.Errorf("connection timeout")

	// Layer 2: Repository layer wraps and adds context
	repoErr := errors.Wrap(dbErr, errors.CodeDatabase, "failed to query users")
	repoErr = errors.WithContext(repoErr, "table", "users")

	// Layer 3: Service layer wraps with business error
	svcErr := errors.Wrap(repoErr, errors.CodeNotFound, "user not found")
	svcErr = errors.WithContext(svcErr, "user_id", "12345")

	// Check if retryable
	fmt.Println("Retryable:", errors.IsRetryable(svcErr))

	// Get error code
	fmt.Println("Code:", errors.GetCode(svcErr))

	// Serialize for API
	response := errors.ToJSON(svcErr)
	fmt.Println("API Code:", response.Code)
	fmt.Println("API Message:", response.Message)

}
Output:
Retryable: true
Code: NOT_FOUND
API Code: NOT_FOUND
API Message: user not found

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func As

func As(err error, target interface{}) bool

As finds the first error in err's chain that matches target. This is a convenience wrapper around the standard library errors.As.

Example:

var platformErr PlatformError
if errors.As(err, &platformErr) {
    code := platformErr.Code()
}
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	err := errors.New(errors.CodeTimeout, "request timeout")
	wrapped := errors.Wrap(err, errors.CodeInternal, "internal error")

	// Extract PlatformError from chain
	var platformErr errors.PlatformError
	if errors.As(wrapped, &platformErr) {
		fmt.Println("Code:", platformErr.Code())
		fmt.Println("Retryable:", platformErr.Classification().IsRetryable())
	}

}
Output:
Code: INTERNAL_ERROR
Retryable: true

func Is

func Is(err, target error) bool

Is reports whether any error in err's chain matches target. This is a convenience wrapper around the standard library errors.Is.

Example:

var ErrNotFound = errors.New(errors.CodeNotFound, "not found")
if errors.Is(err, ErrNotFound) {
    // Handle not found case
}
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	// Create sentinel error
	var ErrUserNotFound = errors.New(errors.CodeNotFound, "user not found")

	// Wrap the sentinel
	err := errors.Wrap(ErrUserNotFound, errors.CodeDatabase, "query failed")

	// Check if error chain contains sentinel
	if errors.Is(err, ErrUserNotFound) {
		fmt.Println("User not found in chain")
	}

}
Output:
User not found in chain

func IsRetryable

func IsRetryable(err error) bool

IsRetryable returns true if the error is classified as retryable. Returns false if the error is nil or not a PlatformError (safe default).

This is the primary function for making retry decisions in the platform. It provides a simple boolean check to determine if an operation should be retried after a failure.

Example:

if errors.IsRetryable(err) {
    // Implement retry with backoff
    time.Sleep(backoff)
    return retry(operation)
}
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	// Retryable error
	timeoutErr := errors.New(errors.CodeTimeout, "request timeout")
	fmt.Println("Timeout retryable:", errors.IsRetryable(timeoutErr))

	// Permanent error
	notFoundErr := errors.New(errors.CodeNotFound, "user not found")
	fmt.Println("NotFound retryable:", errors.IsRetryable(notFoundErr))

}
Output:
Timeout retryable: true
NotFound retryable: false
Example (RetryLoop)
package main

import (
	"fmt"
	"time"

	"github.com/jmgilman/go/errors"
)

func main() {
	operation := func() error {
		// Simulate operation that might fail
		return errors.New(errors.CodeNetwork, "connection refused")
	}

	const maxRetries = 3
	var err error

	for attempt := 0; attempt < maxRetries; attempt++ {
		err = operation()
		if err == nil {
			fmt.Println("Success")
			return
		}

		if !errors.IsRetryable(err) {
			fmt.Println("Permanent error, not retrying")
			return
		}

		fmt.Printf("Attempt %d failed, retrying...\n", attempt+1)
		time.Sleep(100 * time.Millisecond) // Backoff
	}

	fmt.Println("Max retries exceeded")
}
Output:
Attempt 1 failed, retrying...
Attempt 2 failed, retrying...
Attempt 3 failed, retrying...
Max retries exceeded

Types

type ErrorClassification

type ErrorClassification string

ErrorClassification indicates whether an error should trigger a retry. This is used by platform services to determine if an operation should be retried or if it represents a permanent failure.

const (
	// ClassificationRetryable indicates temporary failures that may succeed on retry.
	// Examples: network timeouts, rate limits, transient database issues.
	ClassificationRetryable ErrorClassification = "RETRYABLE"

	// ClassificationPermanent indicates failures that will not succeed on retry.
	// Examples: validation errors, permission denials, resource not found.
	ClassificationPermanent ErrorClassification = "PERMANENT"
)

func GetClassification

func GetClassification(err error) ErrorClassification

GetClassification extracts the ErrorClassification from an error. Returns ClassificationPermanent if the error is nil or not a PlatformError. This is a safe default that prevents inappropriate retry attempts.

This function handles the error chain and will extract the classification from the outermost PlatformError in the chain.

Example:

classification := errors.GetClassification(err)
if classification == errors.ClassificationRetryable {
    // Retry logic
}

func (ErrorClassification) IsRetryable

func (c ErrorClassification) IsRetryable() bool

IsRetryable returns true if the classification indicates retry should be attempted.

type ErrorCode

type ErrorCode string

ErrorCode represents a specific error condition. Error codes are string-based for debuggability and natural JSON serialization.

const (

	// CodeNotFound indicates a requested resource does not exist.
	CodeNotFound ErrorCode = "NOT_FOUND"

	// CodeAlreadyExists indicates a resource already exists and cannot be created again.
	CodeAlreadyExists ErrorCode = "ALREADY_EXISTS"

	// CodeConflict indicates a resource state conflict that prevents the operation.
	CodeConflict ErrorCode = "CONFLICT"

	// CodeUnauthorized indicates the request lacks valid authentication credentials.
	CodeUnauthorized ErrorCode = "UNAUTHORIZED"

	// CodeForbidden indicates the authenticated user lacks permission for the operation.
	CodeForbidden ErrorCode = "FORBIDDEN"

	// CodeInvalidInput indicates the provided input is invalid or malformed.
	CodeInvalidInput ErrorCode = "INVALID_INPUT"

	// CodeInvalidConfig indicates a configuration error prevents the operation.
	CodeInvalidConfig ErrorCode = "INVALID_CONFIGURATION"

	// CodeSchemaFailed indicates the data failed schema validation.
	CodeSchemaFailed ErrorCode = "SCHEMA_VALIDATION_FAILED"

	// CodeDatabase indicates a database operation failed.
	CodeDatabase ErrorCode = "DATABASE_ERROR"

	// CodeNetwork indicates a network operation failed.
	CodeNetwork ErrorCode = "NETWORK_ERROR"

	// CodeTimeout indicates an operation exceeded its time limit.
	CodeTimeout ErrorCode = "TIMEOUT"

	// CodeRateLimit indicates the rate limit has been exceeded.
	CodeRateLimit ErrorCode = "RATE_LIMIT_EXCEEDED"

	// CodeExecutionFailed indicates a general execution failure.
	CodeExecutionFailed ErrorCode = "EXECUTION_FAILED"

	// CodeBuildFailed indicates a build operation failed.
	CodeBuildFailed ErrorCode = "BUILD_FAILED"

	// CodePublishFailed indicates a publish operation failed.
	CodePublishFailed ErrorCode = "PUBLISH_FAILED"

	// CodeCUELoadFailed indicates CUE file/module loading failed.
	CodeCUELoadFailed ErrorCode = "CUE_LOAD_FAILED"

	// CodeCUEBuildFailed indicates CUE build/evaluation failed.
	CodeCUEBuildFailed ErrorCode = "CUE_BUILD_FAILED"

	// CodeCUEValidationFailed indicates CUE validation failed.
	CodeCUEValidationFailed ErrorCode = "CUE_VALIDATION_FAILED"

	// CodeCUEDecodeFailed indicates CUE to Go struct decoding failed.
	CodeCUEDecodeFailed ErrorCode = "CUE_DECODE_FAILED"

	// CodeCUEEncodeFailed indicates CUE to YAML/JSON encoding failed.
	CodeCUEEncodeFailed ErrorCode = "CUE_ENCODE_FAILED"

	// CodeSchemaVersionIncompatible indicates incompatible major schema version.
	// Config major version does not match supported schema major version.
	CodeSchemaVersionIncompatible ErrorCode = "SCHEMA_VERSION_INCOMPATIBLE"

	// CodeInternal indicates an internal system error occurred.
	CodeInternal ErrorCode = "INTERNAL_ERROR"

	// CodeNotImplemented indicates the requested functionality is not implemented.
	CodeNotImplemented ErrorCode = "NOT_IMPLEMENTED"

	// CodeUnavailable indicates the service is temporarily unavailable.
	CodeUnavailable ErrorCode = "SERVICE_UNAVAILABLE"

	// CodeUnknown indicates an unknown or unclassified error occurred.
	CodeUnknown ErrorCode = "UNKNOWN"
)

func GetCode

func GetCode(err error) ErrorCode

GetCode extracts the ErrorCode from an error. Returns CodeUnknown if the error is nil or not a PlatformError.

This function handles the error chain and will extract the code from the outermost PlatformError in the chain.

Example:

if errors.GetCode(err) == errors.CodeNotFound {
    // Handle not found
}
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	err := errors.New(errors.CodeNotFound, "user not found")

	if errors.GetCode(err) == errors.CodeNotFound {
		fmt.Println("Handle not found case")
	}

}
Output:
Handle not found case

type ErrorResponse

type ErrorResponse struct {
	// Code is the error code identifying the type of error.
	Code string `json:"code"`

	// Message is the human-readable error message.
	Message string `json:"message"`

	// Classification indicates whether the error is retryable or permanent.
	Classification string `json:"classification"`

	// Context contains optional metadata about the error.
	// Omitted from JSON if empty.
	Context map[string]interface{} `json:"context,omitempty"`
}

ErrorResponse represents the JSON structure for error responses in API endpoints. It provides a flat, serializable representation of errors without exposing internal error chains or sensitive information.

The wrapped error chain is intentionally excluded to prevent information leakage while still providing useful debugging context through the Code, Message, and Context fields.

func ToJSON

func ToJSON(err error) *ErrorResponse

ToJSON converts any error to an ErrorResponse suitable for JSON serialization. Returns nil if err is nil.

For PlatformError instances, extracts code, message, classification, and context. For standard errors, uses CodeUnknown, ClassificationPermanent, and the error message.

The wrapped error chain is intentionally excluded to prevent information leakage. Security consideration: Error chains may contain internal implementation details, stack traces, database queries, file paths, or other sensitive information.

Example:

func handleError(w http.ResponseWriter, err error) {
    response := errors.ToJSON(err)
    if response == nil {
        return // No error
    }
    w.Header().Set("Content-Type", "application/json")
    statusCode := getHTTPStatus(response.Code)
    w.WriteHeader(statusCode)
    json.NewEncoder(w).Encode(response)
}
Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	err := errors.New(errors.CodeInvalidInput, "validation failed")
	err = errors.WithContext(err, "field", "email")
	err = errors.WithContext(err, "reason", "invalid format")

	response := errors.ToJSON(err)
	jsonBytes, _ := json.MarshalIndent(response, "", "  ")
	fmt.Println(string(jsonBytes))

}
Output:
{
  "code": "INVALID_INPUT",
  "message": "validation failed",
  "classification": "PERMANENT",
  "context": {
    "field": "email",
    "reason": "invalid format"
  }
}
Example (HttpHandler)
package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/jmgilman/go/errors"
)

func main() {
	// Example HTTP error handler
	handleError := func(w http.ResponseWriter, err error) {
		response := errors.ToJSON(err)

		w.Header().Set("Content-Type", "application/json")

		// Map error code to HTTP status
		statusCode := http.StatusInternalServerError
		switch errors.GetCode(err) {
		case errors.CodeNotFound:
			statusCode = http.StatusNotFound
		case errors.CodeUnauthorized:
			statusCode = http.StatusUnauthorized
		case errors.CodeInvalidInput:
			statusCode = http.StatusBadRequest
		}

		w.WriteHeader(statusCode)
		_ = json.NewEncoder(w).Encode(response)
	}

	// Simulate error
	err := errors.New(errors.CodeNotFound, "resource not found")

	// Would write HTTP response
	_ = handleError
	fmt.Println(errors.GetCode(err))
}
Output:
NOT_FOUND

type PlatformError

type PlatformError interface {
	error

	// Code returns the error code identifying the type of error.
	Code() ErrorCode

	// Classification returns whether the error is retryable or permanent.
	Classification() ErrorClassification

	// Message returns the human-readable error message.
	Message() string

	// Context returns attached metadata as a read-only map.
	// Returns nil if no context has been attached.
	Context() map[string]interface{}

	// Unwrap returns the wrapped error for errors.Is and errors.As compatibility.
	// Returns nil if this error does not wrap another error.
	Unwrap() error
}

PlatformError extends the standard error interface with structured information for consistent error handling.

PlatformError provides error codes for categorization, classification for retry logic, contextual metadata, and compatibility with standard library error handling (errors.Is, errors.As, errors.Unwrap).

func New

func New(code ErrorCode, message string) PlatformError

New creates a new PlatformError with the given code and message. The error classification is determined by the error code using default mappings.

Example:

err := errors.New(errors.CodeNotFound, "project not found")
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	err := errors.New(errors.CodeNotFound, "user not found")
	fmt.Println(err.Error())
}
Output:
[NOT_FOUND] user not found

func Newf

func Newf(code ErrorCode, format string, args ...interface{}) PlatformError

Newf creates a new PlatformError with a formatted message. The error classification is determined by the error code using default mappings.

Example:

err := errors.Newf(errors.CodeInvalidInput, "project name too long: %d characters (max %d)", len(name), maxLen)
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	userID := "12345"
	err := errors.Newf(errors.CodeNotFound, "user %s not found", userID)
	fmt.Println(err.Error())
}
Output:
[NOT_FOUND] user 12345 not found

func WithClassification

func WithClassification(err error, classification ErrorClassification) PlatformError

WithClassification overrides the classification of an error. Returns a new PlatformError with the specified classification.

This is useful when you need to override the default classification for an error code. For example, marking a normally permanent error as retryable in specific circumstances.

If err is not a PlatformError, it is converted to one with CodeUnknown. Returns nil if err is nil.

Example:

err := errors.New(errors.CodeDatabase, "connection failed")
// Normally retryable, but mark as permanent for this case
err = errors.WithClassification(err, errors.ClassificationPermanent)
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	// Override default classification
	err := errors.New(errors.CodeDatabase, "database error")
	fmt.Println("Default:", errors.IsRetryable(err))

	// Mark as permanent for this specific case
	err = errors.WithClassification(err, errors.ClassificationPermanent)
	fmt.Println("Overridden:", errors.IsRetryable(err))

}
Output:
Default: true
Overridden: false

func WithContext

func WithContext(err error, key string, value interface{}) PlatformError

WithContext adds a single context field to an error. Returns a new PlatformError with the context field added. Existing context fields are preserved.

If err is not a PlatformError, it is converted to one with CodeUnknown. Returns nil if err is nil.

Example:

err := errors.New(errors.CodeBuildFailed, "build failed")
err = errors.WithContext(err, "project", "my-app")
err = errors.WithContext(err, "phase", "test")
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	err := errors.New(errors.CodeBuildFailed, "build failed")
	err = errors.WithContext(err, "project", "api")
	err = errors.WithContext(err, "phase", "test")

	ctx := err.Context()
	fmt.Printf("Project: %s, Phase: %s\n", ctx["project"], ctx["phase"])
}
Output:
Project: api, Phase: test

func WithContextMap

func WithContextMap(err error, ctx map[string]interface{}) PlatformError

WithContextMap adds multiple context fields to an error. Returns a new PlatformError with the context fields merged. Existing context fields are preserved; new fields override existing ones with the same key.

If err is not a PlatformError, it is converted to one with CodeUnknown. Returns nil if err is nil.

Example:

err := errors.New(errors.CodeExecutionFailed, "execution failed")
err = errors.WithContextMap(err, map[string]interface{}{
    "command": "earthly",
    "target":  "+test",
})
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	err := errors.New(errors.CodeExecutionFailed, "execution failed")
	err = errors.WithContextMap(err, map[string]interface{}{
		"command":   "earthly",
		"target":    "+test",
		"exit_code": 1,
	})

	ctx := err.Context()
	fmt.Printf("Command: %s, Exit: %d\n", ctx["command"], ctx["exit_code"])
}
Output:
Command: earthly, Exit: 1

func Wrap

func Wrap(err error, code ErrorCode, message string) PlatformError

Wrap wraps an error with additional context while preserving the original error. The wrapped error is accessible via Unwrap() and compatible with errors.Is and errors.As.

If the wrapped error is a PlatformError, its classification is preserved. Otherwise, the default classification for the error code is used.

Returns nil if err is nil.

Example:

result, err := repo.Clone(ctx, url)
if err != nil {
    return errors.Wrap(err, errors.CodeNetwork, "failed to clone repository")
}
Example
package main

import (
	"fmt"

	"github.com/jmgilman/go/errors"
)

func main() {
	// Simulate database error
	dbErr := fmt.Errorf("connection refused")

	// Wrap with platform error
	err := errors.Wrap(dbErr, errors.CodeDatabase, "failed to connect to database")

	fmt.Println(errors.GetCode(err))
}
Output:
DATABASE_ERROR

func WrapWithContext

func WrapWithContext(err error, code ErrorCode, message string, ctx map[string]interface{}) PlatformError

WrapWithContext wraps an error and attaches context metadata in a single operation. The context map is copied to prevent external mutation.

Returns nil if err is nil.

Example:

if err := build(ctx); err != nil {
    return errors.WrapWithContext(err, errors.CodeBuildFailed, "build failed", map[string]interface{}{
        "project": projectName,
        "phase":   "test",
    })
}

func Wrapf

func Wrapf(err error, code ErrorCode, format string, args ...interface{}) PlatformError

Wrapf wraps an error with a formatted message while preserving the original error.

Returns nil if err is nil.

Example:

if err := validate(input); err != nil {
    return errors.Wrapf(err, errors.CodeInvalidInput, "validation failed for field %s", fieldName)
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL