Skip to content

Error Handling

See also: codex package on pkg.go.dev

Runnable demos: examples/error-types · examples/decode-errors

All decode failures are structured types. Use errors.As to inspect them precisely, or pass them directly to log/slog — every type implements slog.LogValuer.

Error types

Type Returned by Key fields
ValidationErrors Struct decode []ValidationError; also implements Unwrap() []error
ValidationError each field in Struct decode Field string, Err error
ConstraintError Refine on any codec (Encode and Decode) Name string, Message string
TypeMismatchError any codec receiving wrong Go type Expected string, Got string
ElementError SliceOf decode Index int, Err error
KeyError StringMap / Map decode Key string, Err error
UnknownVariantError TaggedUnion when tag value has no matching codec Tag string, Variant string
VariantError TaggedUnion when a known variant fails to decode/encode Tag string, Variant string, Err error
ErrMissingField required Field when key absent sentinel; use errors.Is

Inspecting errors with errors.As

var ve codex.ValidationErrors
if errors.As(err, &ve) {
    for _, fieldErr := range ve {
        var ce codex.ConstraintError
        if errors.As(fieldErr.Err, &ce) {
            // ce.Name    — constraint identifier, e.g. "email", "minLen(3)"
            // ce.Message — human-readable description of the failure
            fmt.Printf("field %q: constraint %q failed: %s\n",
                fieldErr.Field, ce.Name, ce.Message)
        }
        if errors.Is(fieldErr.Err, codex.ErrMissingField) {
            fmt.Printf("field %q is required but absent\n", fieldErr.Field)
        }
    }
}

Structured logging with log/slog

All error types implement slog.LogValuer. Pass them as slog attributes to get structured key-value output:

logger := slog.Default().With("transport", "http")

var ve codex.ValidationErrors
if errors.As(err, &ve) {
    // Emits each field name and its error as separate slog attributes.
    logger.Error("request validation failed", slog.Any("validation_errors", ve))

    for _, fieldErr := range ve {
        var ce codex.ConstraintError
        if errors.As(fieldErr.Err, &ce) {
            // Emits field.field, field.error, constraint.constraint, constraint.message.
            logger.Warn("field constraint failed",
                slog.Any("field", fieldErr),
                slog.Any("constraint", ce),
            )
        }
    }
}

HTTP adapter errors (api/rest + adapters/nethttp)

Error type When returned errors.As target
rest.PathParamError path variable fails its codec PathParamError{Name, Value, Err}
rest.MissingPathVarError template variable absent from vars map MissingPathVarError{Name}
rest.QueryParamError query parameter value fails its codec QueryParamError{Name, Value, Err}
rest.CookieParamError cookie value fails its codec CookieParamError{Name, Value, Err}
rest.HeaderParamError request header value fails its codec HeaderParamError{Name, Value, Err}
rest.ResponseHeaderParamError response header fails codec (adapter returns 500) ResponseHeaderParamError{Name, Value, Err}
rest.ResponseCookieParamError response cookie fails codec (adapter returns 500) ResponseCookieParamError{Name, Value, Err}
rest.UnsupportedMediaTypeError wrong Content-Type on POST/PUT/PATCH UnsupportedMediaTypeError{Got, Supported}
rest.NotAcceptableError Accept header has no match NotAcceptableError{Accept, Supported}
rest.BodyTooLargeError body exceeds Options.MaxBodyBytes BodyTooLargeError{Limit}
rest.SecurityCredentialError credential codec validation failure SecurityCredentialError{Scheme, Err}
rest.SecurityError SecurityFunc rejected the request SecurityError{Err}

HTTP client errors (adapters/nethttp.Call)

Error type When returned
nethttp.UnexpectedStatusError{Method, Path, StatusCode, Body} non-2xx response
nethttp.RequestBuildError{Err} http.NewRequestWithContext failure
nethttp.RequestError{Method, Path, Err} transport failure (network, DNS, TLS)
nethttp.ResponseBodyError{Err} io.ReadAll failure on response body
var statusErr nethttp.UnexpectedStatusError
if errors.As(err, &statusErr) {
    slog.Error("api call failed",
        "method", statusErr.Method,
        "path",   statusErr.Path,
        "status", statusErr.StatusCode,
        "body",   string(statusErr.Body),
    )
}

MQTT adapter errors (adapters/mqtt)

Error type When returned
events.TopicParamError{Name, Value, Err} topic variable fails its codec
events.MissingTopicVarError{Name} topic variable absent from vars map
amqtt.TopicMismatchError{Template, Topic} concrete topic doesn't match template structure
amqtt.SubscribeHandler(ctx, channel, handler, amqtt.SubscribeOptions{
    OnError: func(e amqtt.SubscribeError) {
        switch e.Kind {
        case amqtt.KindDecode:
            var validationErrs codex.ValidationErrors
            if errors.As(e.Err, &validationErrs) {
                logger.Warn("decode validation error",
                    "topic", e.Topic,
                    "errors", validationErrs, // triggers ValidationErrors.LogValue()
                )
            }
        case amqtt.KindHandler:
            logger.Error("handler error", "topic", e.Topic, "error", e.Err)
        }
    },
})

Forge pipeline errors (forge)

Error type When
forge.InputError{Err} input codec validation failed
forge.RefinementError{Function, Err} RefineFunc or WithRefinement constraint failed
forge.ApplyError{Function, Err} compute function returned an error
forge.OutputError{Err} output codec validation failed
forge.CollectionElementError{Index, Function, Err} slice collection op failed at element
forge.CollectionKeyError{Key, Function, Err} map collection op failed at key

MCP errors (api/mcp)

Error type When
mcp.ToolInputError{Name, Err} ToolHandle.Decode — input codec failure
mcp.ToolOutputError{Name, Err} ToolHandle.Encode — output codec failure
mcp.ResourceParamError{Name, Value, Err} URI variable fails its codec
mcp.MissingResourceVarError{Name} required URI variable absent
mcp.PromptArgError{Name, Err} prompt argument codec failure
mcp.MissingPromptArgError{Name} required prompt argument absent

See also