Help us shape the future of Go! Take the 2026 Go Developer Survey

errormap

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 6 Imported by: 0

README

Error Map

arkoren.dev/core/http/middleware/errormap translates errors returned by handlers into RFC 9457 problem details. Arkoren's own HTTP errors are already problem.Details; domain and contract sentinels such as database.ErrNoRows or storage.ErrNotFound are plain errors that the router renders as 500. Map them once here instead of in every handler.

On an existing r router, map missing rows and objects to HTTP 404. In addition to this package, import arkoren.dev/core/service/database, arkoren.dev/core/service/storage, arkoren.dev/core/http/request, and arkoren.dev/foundation/problem:

r.Use(errormap.Middleware(
	errormap.Map(database.ErrNoRows, problem.NotFound()),
	errormap.Map(storage.ErrNotFound, problem.NotFound()),
	errormap.Func(request.ValidationProblem),
))

Mappings run in order and the first one that handles the error wins. Map wraps the original error, so logging and tracing still see the cause and errors.Is still matches the original sentinel. Custom Func mappings own cause preservation. Register the middleware first so it sees errors returned by later middleware. The router's own unmatched 404 and 405 errors pass through router-wide middleware, including this mapper. Middleware scoped only to a matched group or route does not wrap an unmatched request.

errormap.Func(request.ValidationProblem) renders a raw validate.Errors as the 422 validation problem. A mapping that returns nil or its input unchanged does not handle the error, so it works in any position. Unchanged detection compares values and retained references, not errors.Is categories: a derived problem with different detail, headers, or cause is a replacement even if its type, status, and title match.

Return nil when you need to decline without relying on equality. Comparable errors use Go equality; non-comparable structs and arrays compare their fields and elements recursively. Maps retain storage identity, and slices retain backing-storage, length, and capacity identity rather than comparing contents. Separate value errors with equal fields are indistinguishable; non-nil functions and NaNs cannot compare equal. Pointer errors preserve identity even when their fields contain those values. Do not mutate a mapping's input: the middleware does not snapshot errors before invoking mappings.

The middleware copies the ordered mapping slice at construction, so changing the caller's slice cannot replace published mappings. Functions and their captured values remain shared.

See the error handling guide for application error responses.

Documentation

Overview

Package errormap provides middleware that translates errors returned by handlers into HTTP problems.

Arkoren's own errors that carry HTTP meaning are already problem.Details. Domain and contract sentinels such as database.ErrNoRows or storage.ErrNotFound are plain errors, so the router renders them as 500. errormap is where an application decides what they mean over HTTP:

r.Use(errormap.Middleware(
	errormap.Map(database.ErrNoRows, problem.NotFound()),
	errormap.Map(storage.ErrNotFound, problem.NotFound()),
	errormap.Func(request.ValidationProblem),
))

Mappings run in order and the first one that handles the error wins. Map keeps the original error in the replacement's chain, so logging and tracing still see the cause; Func mappings own their cause preservation. A mapping that returns nil or its input unchanged, such as request.ValidationProblem for other errors, passes the error on to the next one.

Unchanged detection compares values and retained references, not error categories. A derived problem with different detail, headers, or cause is handled even when errors.Is matches the input. See Mapping for limits of detecting unchanged value errors; returning nil always declines a mapping. Middleware copies the ordered mapping slice at construction, but functions and their captured values remain shared.

Placement

Register errormap first with Use, so it wraps every later middleware and sees the errors they return. Errors the router produces for unmatched routes (404) and wrong methods (405) pass through router-wide middleware, including this mapper. Middleware attached only to a matched group or route does not wrap unmatched requests on the parent router.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Middleware

func Middleware(mappings ...Mapping) router.Middleware

Middleware returns middleware that maps errors returned by the next handler. Mappings are tried in the order given and the first one that returns an error other than nil or its unchanged input wins. Each mapping receives the original handler error, not a previous mapping's result. An unhandled error and a nil error pass through unchanged. Errors are only mapped when the handler returns them; a response already written is left alone.

The ordered mapping slice is copied at construction; functions and their captured values remain shared. See Mapping for unchanged-value semantics.

Register it first with Use so it sees the errors of every later middleware, including router-generated 404 and 405 problems at router-wide scope. Calling Middleware with no mappings is a no-op.

Example:

r.Use(errormap.Middleware(
	errormap.Map(database.ErrNoRows, problem.NotFound()),
))

Types

type Mapping

type Mapping func(err error) error

Mapping translates an error into another error. It returns nil, or its input unchanged, when it does not handle the error, so Middleware tries the next mapping. Build one with Map or Func. Matching an input with errors.Is does not make a replacement unchanged.

Comparable errors use equality. Non-comparable values compare their fields and elements recursively, with maps and slices compared by storage identity rather than contents. Separately constructed value errors with equal fields are indistinguishable, and non-nil functions and NaNs cannot compare equal. Return nil to decline reliably for those cases, or use pointer errors when replacement identity matters. Mappings must not mutate their input; detection does not snapshot errors.

Example:

mapping := errormap.Map(database.ErrNoRows, problem.NotFound())
r.Use(errormap.Middleware(mapping))

func Func

func Func(fn func(err error) error) Mapping

Func returns a Mapping backed by fn, for conversions that need to inspect the error, such as extracting a typed error with errors.As. fn returns the replacement error, or nil or the unchanged input to leave the error unhandled and try the next mapping. See Mapping for unchanged-value comparison limits. fn owns cause preservation for its replacement; unlike Map, Func does not attach the original error automatically.

The signature matches request.ValidationProblem, so errormap.Func(request.ValidationProblem) converts validate.Errors to a 422 problem and passes every other error on, in any position.

Example:

errormap.Func(func(err error) error {
	if conflict, ok := errors.AsType[*ConflictError](err); ok {
		return problem.Conflict().Derive(func(details *problem.Builder) {
			details.Detail(conflict.Reason)
			details.Error(err)
		})
	}

	return nil
})

func Map

func Map(target error, replacement problem.Details) Mapping

Map returns a Mapping that replaces any error matching target, according to errors.Is, with replacement. The replacement wraps the original error (problem.Builder.Error in problem.Details.Derive), so the cause stays available to logging and tracing and still matches target with errors.Is.

replacement is a problem.Details rather than an error because attaching the cause uses a derivation callback; any other error type could not carry it.

Example:

errormap.Map(database.ErrNoRows, problem.NotFound().Derive(func(details *problem.Builder) {
	details.Detail("No such record.")
}))

Jump to

Keyboard shortcuts

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