apperror

package
v0.11.2 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package apperror defines the typed application error that handlers, services, and repositories return instead of writing HTTP error responses inline. A single mapper at the routing layer (middleware.MapErrors) catches the error, logs it, and serializes it onto the wire via WriteHTTP — see resources/ERROR_HANDLING_IMPLEMENTATION_PLAN.md.

Index

Constants

View Source
const (
	CodeCommonValidationFailed    = "VALIDATION_FAILED"
	CodeCommonUnauthorized        = "UNAUTHORIZED"
	CodeCommonForbidden           = "FORBIDDEN"
	CodeCommonNotFound            = "NOT_FOUND"
	CodeCommonConflict            = "CONFLICT"
	CodeCommonNotAcceptable       = "NOT_ACCEPTABLE"
	CodeCommonUnprocessableEntity = "UNPROCESSABLE_ENTITY"
	CodeCommonInternalError       = "INTERNAL_ERROR"
	CodeCommonServiceUnavailable  = "SERVICE_UNAVAILABLE"
	CodeCommonTooManyRequests     = "TOO_MANY_REQUESTS"
)

Common, domain-agnostic catalog codes. These mirror the codes documented as shared response examples in resources/openapi.yaml, and are the fallback used by NewErrorResponse when a handler hasn't been migrated to a more specific domain code via NewErrorResponseWithCode.

View Source
const (
	CodeLLMProviderNotFound                   = "LLM_PROVIDER_NOT_FOUND"
	CodeLLMProviderExists                     = "LLM_PROVIDER_EXISTS"
	CodeLLMProviderLimitReached               = "LLM_PROVIDER_LIMIT_REACHED"
	CodeLLMProviderAPIKeyNotFound             = "LLM_PROVIDER_API_KEY_NOT_FOUND"
	CodeLLMProviderDeploymentValidationFailed = "LLM_PROVIDER_DEPLOYMENT_VALIDATION_FAILED"
	CodeLLMProxyNotFound                      = "LLM_PROXY_NOT_FOUND"
	CodeLLMProxyExists                        = "LLM_PROXY_EXISTS"
	CodeLLMProxyLimitReached                  = "LLM_PROXY_LIMIT_REACHED"
	CodeLLMProxyAPIKeyNotFound                = "LLM_PROXY_API_KEY_NOT_FOUND"
	CodeLLMProviderRefNotFound                = "LLM_PROVIDER_REF_NOT_FOUND"
	CodeLLMProxyDeploymentValidationFailed    = "LLM_PROXY_DEPLOYMENT_VALIDATION_FAILED"
	CodeLLMProviderAPIKeyForbidden            = "LLM_PROVIDER_API_KEY_FORBIDDEN"
	CodeLLMProxyAPIKeyForbidden               = "LLM_PROXY_API_KEY_FORBIDDEN"
)

LLM provider/proxy domain codes, matching the examples documented in resources/openapi.yaml for the corresponding operations.

View Source
const (
	CodeLLMProviderTemplateNotFound          = "LLM_PROVIDER_TEMPLATE_NOT_FOUND"
	CodeLLMProviderTemplateVersionNotFound   = "LLM_PROVIDER_TEMPLATE_VERSION_NOT_FOUND"
	CodeLLMProviderTemplateRefNotFound       = "LLM_PROVIDER_TEMPLATE_REF_NOT_FOUND"
	CodeLLMProviderTemplateExists            = "LLM_PROVIDER_TEMPLATE_EXISTS"
	CodeLLMProviderTemplateVersionExists     = "LLM_PROVIDER_TEMPLATE_VERSION_EXISTS"
	CodeLLMProviderTemplateManagedByReserved = "LLM_PROVIDER_TEMPLATE_MANAGED_BY_RESERVED"
	CodeLLMProviderTemplateInUse             = "LLM_PROVIDER_TEMPLATE_IN_USE"
	CodeLLMProviderTemplateReadOnly          = "LLM_PROVIDER_TEMPLATE_READ_ONLY"
	CodeLLMProviderTemplateNotToggleable     = "LLM_PROVIDER_TEMPLATE_NOT_TOGGLEABLE"
)

LLM provider template domain codes. The *_VERSION_* and *_REF_* codes keep one HTTP status per code in the apperror catalog: NOT_FOUND codes are 404s for the resource targeted by the URL, REF_NOT_FOUND codes are 400s for a resource referenced in the request body, and VERSION codes distinguish a missing/duplicate version from a missing/duplicate template.

View Source
const (
	CodeGatewayNotFound              = "GATEWAY_NOT_FOUND"
	CodeGatewayTokenNotFound         = "GATEWAY_TOKEN_NOT_FOUND"
	CodeGatewayConnectionUnavailable = "GATEWAY_CONNECTION_UNAVAILABLE"
	CodeGatewayHasActiveDeployments  = "GATEWAY_HAS_ACTIVE_DEPLOYMENTS"
	CodeGatewayNameConflict          = "GATEWAY_NAME_CONFLICT"
	CodeGatewayTokenLimitReached     = "GATEWAY_TOKEN_LIMIT_REACHED"
)

Gateway domain codes, matching the examples documented in resources/openapi.yaml.

View Source
const (
	CodeDeploymentBaseNotFound    = "DEPLOYMENT_BASE_NOT_FOUND"
	CodeDeploymentRestoreConflict = "DEPLOYMENT_RESTORE_CONFLICT"
	CodeDeploymentNotFound        = "DEPLOYMENT_NOT_FOUND"
	CodeDeploymentNotActive       = "DEPLOYMENT_NOT_ACTIVE"
	CodeDeploymentGatewayMismatch = "DEPLOYMENT_GATEWAY_MISMATCH"
	CodeDeploymentActive          = "DEPLOYMENT_ACTIVE"
	CodeDeploymentInvalidStatus   = "DEPLOYMENT_INVALID_STATUS"
)

Deployment domain codes, shared across REST API / LLM provider / LLM proxy / MCP proxy deployment operations (identical conditions across all four).

View Source
const (
	CodeRESTAPINotFound                   = "REST_API_NOT_FOUND"
	CodeRESTAPIExists                     = "REST_API_EXISTS"
	CodeRESTAPIDeploymentValidationFailed = "REST_API_DEPLOYMENT_VALIDATION_FAILED"
)

REST API domain codes.

View Source
const (
	CodeMCPProxyNotFound                   = "MCP_PROXY_NOT_FOUND"
	CodeMCPProxyExists                     = "MCP_PROXY_EXISTS"
	CodeMCPProxyLimitReached               = "MCP_PROXY_LIMIT_REACHED"
	CodeMCPProxyDeploymentValidationFailed = "MCP_PROXY_DEPLOYMENT_VALIDATION_FAILED"
)

MCP proxy domain codes.

View Source
const (
	CodeOrganizationNotFound = "ORGANIZATION_NOT_FOUND"
	CodeOrganizationExists   = "ORGANIZATION_EXISTS"
)

Organization domain codes.

View Source
const (
	CodeProjectNotFound    = "PROJECT_NOT_FOUND"
	CodeProjectRefNotFound = "PROJECT_REF_NOT_FOUND"
	CodeProjectExists      = "PROJECT_EXISTS"
)

Project domain codes. PROJECT_REF_NOT_FOUND is the 400 counterpart of PROJECT_NOT_FOUND for a project referenced in a request body (e.g. the projectId in a create request) rather than targeted by the URL.

View Source
const (
	CodeApplicationNotFound = "APPLICATION_NOT_FOUND"
	CodeApplicationExists   = "APPLICATION_EXISTS"
)

Application domain codes.

View Source
const (
	CodeSubscriptionNotFound     = "SUBSCRIPTION_NOT_FOUND"
	CodeSubscriptionExists       = "SUBSCRIPTION_EXISTS"
	CodeSubscriptionForbidden    = "SUBSCRIPTION_FORBIDDEN"
	CodeSubscriptionPlanNotFound = "SUBSCRIPTION_PLAN_NOT_FOUND"
	CodeSubscriptionPlanExists   = "SUBSCRIPTION_PLAN_EXISTS"
)

Subscription domain codes.

View Source
const (
	CodePolicyVersionConflict = "POLICY_VERSION_CONFLICT"
	CodePolicyInvalidState    = "POLICY_INVALID_STATE"
	CodePolicyInUse           = "POLICY_IN_USE"
)

Custom policy domain codes.

View Source
const (
	CodeSecretNotFound = "SECRET_NOT_FOUND"
	CodeSecretExists   = "SECRET_EXISTS"
	CodeSecretInUse    = "SECRET_IN_USE"
)

Secret domain codes.

View Source
const (
	CodeArtifactNotFound         = "ARTIFACT_NOT_FOUND"
	CodeArtifactExists           = "ARTIFACT_EXISTS"
	CodeArtifactReadOnly         = "ARTIFACT_READ_ONLY"
	CodeArtifactRuntimeImmutable = "ARTIFACT_RUNTIME_IMMUTABLE"
	CodeArtifactDeployed         = "ARTIFACT_DEPLOYED"
)

Artifact domain codes. Used by flows that operate on a generic artifact reference (REST API / LLM provider / LLM proxy / MCP proxy) — API keys, subscriptions, and application associations — where the caller shouldn't need to know which concrete artifact kind was targeted, and by the data-plane-origin guard that protects DP-originated artifacts from control-plane mutation.

View Source
const (
	CodeCustomPolicyNotFound        = "CUSTOM_POLICY_NOT_FOUND"
	CodeCustomPolicyVersionNotFound = "CUSTOM_POLICY_VERSION_NOT_FOUND"
)

Custom policy domain codes.

View Source
const (
	CodeApplicationAPIKeyNotFound  = "APPLICATION_API_KEY_NOT_FOUND"
	CodeApplicationAPIKeyForbidden = "APPLICATION_API_KEY_FORBIDDEN"
)

Application API key domain codes (application-scoped API key mappings, distinct from the LLM provider/proxy API key codes above).

Variables

View Source
var (
	Unauthorized        = def(CodeCommonUnauthorized, http.StatusUnauthorized, "Invalid or expired credentials.")
	Forbidden           = def(CodeCommonForbidden, http.StatusForbidden, "You do not have permission to perform this action.")
	ValidationFailed    = def(CodeCommonValidationFailed, http.StatusBadRequest, "%s")
	NotFound            = def(CodeCommonNotFound, http.StatusNotFound, "The requested resource could not be found.")
	Conflict            = def(CodeCommonConflict, http.StatusConflict, "The request conflicts with the current state of the resource.")
	NotAcceptable       = def(CodeCommonNotAcceptable, http.StatusNotAcceptable, "The requested media type is not supported.")
	UnprocessableEntity = def(CodeCommonUnprocessableEntity, http.StatusUnprocessableEntity, "The request could not be processed.")
	Internal            = def(CodeCommonInternalError, http.StatusInternalServerError, "An unexpected error occurred.")
	ServiceUnavailable  = def(CodeCommonServiceUnavailable, http.StatusServiceUnavailable, "The service is temporarily unavailable.")
	TooManyRequests     = def(CodeCommonTooManyRequests, http.StatusTooManyRequests, "%s")
)

Common, domain-agnostic entries. Unauthorized is intentionally the ONLY 401 entry, with a fixed message: every authentication failure (missing, expired, invalid, revoked) returns the identical payload per the unified auth-failure rule in error-handling.md; the specific reason travels in the wrapped cause / log message only.

View Source
var (
	RESTAPINotFound = def(CodeRESTAPINotFound, http.StatusNotFound, "The specified REST API could not be found.")
	// RESTAPIExists covers three distinct conflicts (handle, name+version,
	// project) — the call site supplies which one.
	RESTAPIExists                     = def(CodeRESTAPIExists, http.StatusConflict, "%s")
	RESTAPIDeploymentValidationFailed = def(CodeRESTAPIDeploymentValidationFailed, http.StatusBadRequest, "%s")
)

REST API entries.

View Source
var (
	LLMProviderNotFound                   = def(CodeLLMProviderNotFound, http.StatusNotFound, "The specified LLM provider could not be found.")
	LLMProviderRefNotFound                = def(CodeLLMProviderRefNotFound, http.StatusBadRequest, "The referenced LLM provider could not be found.")
	LLMProviderExists                     = def(CodeLLMProviderExists, http.StatusConflict, "An LLM provider with this ID already exists.")
	LLMProviderLimitReached               = def(CodeLLMProviderLimitReached, http.StatusConflict, "LLM provider limit reached for the organization.")
	LLMProviderAPIKeyNotFound             = def(CodeLLMProviderAPIKeyNotFound, http.StatusNotFound, "The specified API key could not be found.")
	LLMProviderAPIKeyForbidden            = def(CodeLLMProviderAPIKeyForbidden, http.StatusForbidden, "You do not have permission to access this API key.")
	LLMProviderDeploymentValidationFailed = def(CodeLLMProviderDeploymentValidationFailed, http.StatusBadRequest, "%s")

	LLMProxyNotFound                   = def(CodeLLMProxyNotFound, http.StatusNotFound, "The specified LLM proxy could not be found.")
	LLMProxyExists                     = def(CodeLLMProxyExists, http.StatusConflict, "An LLM proxy with this ID already exists.")
	LLMProxyLimitReached               = def(CodeLLMProxyLimitReached, http.StatusConflict, "LLM proxy limit reached for the organization.")
	LLMProxyAPIKeyNotFound             = def(CodeLLMProxyAPIKeyNotFound, http.StatusNotFound, "The specified API key could not be found.")
	LLMProxyAPIKeyForbidden            = def(CodeLLMProxyAPIKeyForbidden, http.StatusForbidden, "You do not have permission to access this API key.")
	LLMProxyDeploymentValidationFailed = def(CodeLLMProxyDeploymentValidationFailed, http.StatusBadRequest, "%s")
)

LLM provider / proxy entries.

View Source
var (
	LLMProviderTemplateNotFound          = def(CodeLLMProviderTemplateNotFound, http.StatusNotFound, "The specified LLM provider template could not be found.")
	LLMProviderTemplateVersionNotFound   = def(CodeLLMProviderTemplateVersionNotFound, http.StatusNotFound, "The specified LLM provider template version could not be found.")
	LLMProviderTemplateRefNotFound       = def(CodeLLMProviderTemplateRefNotFound, http.StatusBadRequest, "The referenced LLM provider template could not be found.")
	LLMProviderTemplateExists            = def(CodeLLMProviderTemplateExists, http.StatusConflict, "An LLM provider template with this ID already exists.")
	LLMProviderTemplateVersionExists     = def(CodeLLMProviderTemplateVersionExists, http.StatusConflict, "This template version already exists.")
	LLMProviderTemplateManagedByReserved = def(CodeLLMProviderTemplateManagedByReserved, http.StatusBadRequest, "'wso2' is reserved and cannot be used as managedBy on custom templates.")
	LLMProviderTemplateInUse             = def(CodeLLMProviderTemplateInUse, http.StatusConflict, "This template version is in use by one or more providers.")
	LLMProviderTemplateReadOnly          = def(CodeLLMProviderTemplateReadOnly, http.StatusForbidden, "Built-in templates are read-only and cannot be modified.")
	LLMProviderTemplateNotToggleable     = def(CodeLLMProviderTemplateNotToggleable, http.StatusForbidden, "Only built-in templates can be enabled or disabled.")
)

LLM provider template entries.

View Source
var (
	GatewayNotFound              = def(CodeGatewayNotFound, http.StatusNotFound, "The specified gateway could not be found.")
	GatewayTokenNotFound         = def(CodeGatewayTokenNotFound, http.StatusNotFound, "The specified gateway token could not be found.")
	GatewayConnectionUnavailable = def(CodeGatewayConnectionUnavailable, http.StatusServiceUnavailable, "No gateway connections are currently available.")
	GatewayHasActiveDeployments  = def(CodeGatewayHasActiveDeployments, http.StatusConflict, "The gateway has active deployments and cannot be deleted.")
	GatewayNameConflict          = def(CodeGatewayNameConflict, http.StatusConflict, "A gateway with this name already exists.")
	GatewayTokenLimitReached     = def(CodeGatewayTokenLimitReached, http.StatusConflict, "Gateway token limit reached.")
)

Gateway entries.

View Source
var (
	DeploymentBaseNotFound    = def(CodeDeploymentBaseNotFound, http.StatusNotFound, "The specified base deployment could not be found.")
	DeploymentRestoreConflict = def(CodeDeploymentRestoreConflict, http.StatusConflict, "Cannot restore the currently deployed deployment, or the deployment is invalid.")
	DeploymentNotFound        = def(CodeDeploymentNotFound, http.StatusNotFound, "The specified deployment could not be found.")
	DeploymentNotActive       = def(CodeDeploymentNotActive, http.StatusConflict, "No active deployment found for this %s on the gateway.")
	DeploymentGatewayMismatch = def(CodeDeploymentGatewayMismatch, http.StatusBadRequest, "Deployment is bound to a different gateway.")
	DeploymentActive          = def(CodeDeploymentActive, http.StatusConflict, "Cannot delete an active deployment - undeploy it first.")
	DeploymentInvalidStatus   = def(CodeDeploymentInvalidStatus, http.StatusBadRequest, "The specified deployment status filter is invalid.")
)

Deployment entries, shared across REST API / LLM provider / LLM proxy / MCP proxy deployment operations. DeploymentNotActive's verb is the artifact kind, e.g. "API", "LLM provider".

View Source
var (
	MCPProxyNotFound                   = def(CodeMCPProxyNotFound, http.StatusNotFound, "The specified MCP proxy could not be found.")
	MCPProxyExists                     = def(CodeMCPProxyExists, http.StatusConflict, "An MCP proxy with this ID already exists.")
	MCPProxyLimitReached               = def(CodeMCPProxyLimitReached, http.StatusConflict, "MCP proxy limit reached for the organization.")
	MCPProxyDeploymentValidationFailed = def(CodeMCPProxyDeploymentValidationFailed, http.StatusBadRequest, "%s")
)

MCP proxy entries.

View Source
var (
	OrganizationNotFound = def(CodeOrganizationNotFound, http.StatusNotFound, "The specified organization could not be found.")
	OrganizationExists   = def(CodeOrganizationExists, http.StatusConflict, "An organization with this name already exists.")
	ProjectNotFound      = def(CodeProjectNotFound, http.StatusNotFound, "The specified project could not be found.")
	ProjectRefNotFound   = def(CodeProjectRefNotFound, http.StatusBadRequest, "The referenced project could not be found.")
	ProjectExists        = def(CodeProjectExists, http.StatusConflict, "A project with this name already exists in the organization.")
	ApplicationNotFound  = def(CodeApplicationNotFound, http.StatusNotFound, "The specified application could not be found.")
	ApplicationExists    = def(CodeApplicationExists, http.StatusConflict, "An application with this name already exists.")
)

Organization / project / application entries.

View Source
var (
	SubscriptionNotFound     = def(CodeSubscriptionNotFound, http.StatusNotFound, "The specified subscription could not be found.")
	SubscriptionExists       = def(CodeSubscriptionExists, http.StatusConflict, "A subscription for this application and API already exists.")
	SubscriptionForbidden    = def(CodeSubscriptionForbidden, http.StatusForbidden, "You do not have permission to access this subscription.")
	SubscriptionPlanNotFound = def(CodeSubscriptionPlanNotFound, http.StatusNotFound, "The specified subscription plan could not be found.")
	SubscriptionPlanExists   = def(CodeSubscriptionPlanExists, http.StatusConflict, "A subscription plan with this name already exists.")
)

Subscription entries.

View Source
var (
	PolicyVersionConflict     = def(CodePolicyVersionConflict, http.StatusConflict, "The policy version conflicts with an existing version.")
	PolicyInvalidState        = def(CodePolicyInvalidState, http.StatusConflict, "The policy is not in a valid state for this operation.")
	PolicyInUse               = def(CodePolicyInUse, http.StatusConflict, "The policy is in use and cannot be deleted.")
	CustomPolicyNotFound      = def(CodeCustomPolicyNotFound, http.StatusNotFound, "The specified policy could not be found.")
	CustomPolicyVersionNotFnd = def(CodeCustomPolicyVersionNotFound, http.StatusNotFound, "The specified policy version could not be found.")
)

Custom policy entries.

View Source
var (
	SecretNotFound = def(CodeSecretNotFound, http.StatusNotFound, "The specified secret could not be found.")
	SecretExists   = def(CodeSecretExists, http.StatusConflict, "A secret with this name already exists.")
	SecretInUse    = def(CodeSecretInUse, http.StatusConflict, "The secret is in use and cannot be deleted.")
)

Secret entries.

View Source
var (
	ArtifactNotFound         = def(CodeArtifactNotFound, http.StatusNotFound, "The specified artifact could not be found.")
	ArtifactExists           = def(CodeArtifactExists, http.StatusConflict, "The artifact already exists.")
	ArtifactReadOnly         = def(CodeArtifactReadOnly, http.StatusForbidden, "%s")
	ArtifactRuntimeImmutable = def(CodeArtifactRuntimeImmutable, http.StatusForbidden, "%s")
	ArtifactDeployed         = def(CodeArtifactDeployed, http.StatusConflict, "%s")
)

Artifact entries — generic artifact-reference flows and the data-plane-origin guard. The guard produces client-appropriate messages at the call site, so these are passthrough templates.

View Source
var (
	ApplicationAPIKeyNotFound  = def(CodeApplicationAPIKeyNotFound, http.StatusNotFound, "The specified API key could not be found.")
	ApplicationAPIKeyForbidden = def(CodeApplicationAPIKeyForbidden, http.StatusForbidden, "You do not have permission to access this API key.")
)

Application API key entries.

Functions

func NewValidationErrorResponse

func NewValidationErrorResponse(w http.ResponseWriter, err error)

NewValidationErrorResponse writes a 400 JSON error response for validation errors.

func WriteHTTP

func WriteHTTP(w http.ResponseWriter, e *Error, trackingID string)

WriteHTTP is the one place in the codebase that serializes an *Error onto an http.ResponseWriter as the standard ErrorResponse shape. Both the error mapper (middleware.MapErrors) and the pre-routing auth middleware call it, so the wire format cannot drift between the two paths.

trackingID, when non-empty, is echoed in the response body so the client can quote it back for log correlation; the mapper passes it only for 5xx responses. Callers with no correlation ID to expose pass "".

Types

type Def

type Def struct {
	Code       string // stable, client-visible catalog code, e.g. "REST_API_EXISTS"
	HTTPStatus int
	MessageFmt string // client-facing message template; may contain fmt verbs
}

Def is a catalog entry: the code, HTTP status, and client-facing message template are bound together at declaration time (see catalog.go) so call sites cannot pair a code with the wrong status or a drifting message. It is the Go equivalent of a Java ExceptionCodes enum constant. Errors are created only through Def.New / Def.Wrap (plus NewValidation for validator.ValidationErrors), never by picking a code/status/message triple at the call site.

func (Def) Is

func (d Def) Is(err error) bool

Is reports whether err is (or wraps) an *Error carrying this Def's code, letting callers branch on catalog entries without sentinel errors.

func (Def) New

func (d Def) New(args ...any) *Error

New instantiates the catalog entry for a business-rule failure with no underlying error. args fill the MessageFmt verbs, if any; they must be user-supplied values (names, handles, IDs), never internal error text. The call stack is captured automatically.

func (Def) Wrap

func (d Def) Wrap(cause error, args ...any) *Error

Wrap instantiates the catalog entry for a failure caused by a lower-level error (DB, IO, downstream call). The cause is a required positional parameter — not a fluent afterthought — so it cannot be forgotten; it feeds the mapper's log line and errors.Is/As chains, and is never sent to the client.

type Error

type Error struct {
	Code        string       // catalog code, e.g. CodeCommonValidationFailed
	HTTPStatus  int          // status to write
	Message     string       // client-facing message
	FieldErrors []FieldError // optional, for validation failures
	Details     any          // optional structured metadata
	LogMessage  string       // internal-only detail; never sent to client
	Cause       error        // wrapped original error, for logging/unwrapping
	Stack       []uintptr    // captured at construction time, see New
}

Error carries everything the error mapper needs to log a failure and write the client-facing ErrorResponse: the catalog code and HTTP status, the sanitized client message, optional field-level validation errors and structured details, plus internal-only diagnostics (log message, wrapped cause, and the call stack captured at construction time).

func NewValidation

func NewValidation(err error) *Error

NewValidation converts a request-validation failure into an Error, mirroring NewValidationErrorResponse: validator.ValidationErrors become field-level errors under VALIDATION_FAILED; anything else maps to the generic "Invalid input." message with the original error kept as the cause for internal logging.

func (*Error) Error

func (e *Error) Error() string

Error satisfies the error interface. It intentionally includes only the code and client message — internal diagnostics stay in LogMessage/Cause.

func (*Error) Origin

func (e *Error) Origin() string

Origin symbolizes just the first frame of the stack captured at construction time — the file:line of the Def.New/Def.Wrap call site where the error actually originated, e.g. "/internal/handler/application.go:142". The path is trimmed to start at "/internal/" so logs carry a short, repo-relative path instead of the full build-machine absolute path. This is distinct from slog's own "source" attribute, which always points at the mapper's log call site (middleware/error_mapper.go) regardless of where the error was created, and is therefore useless for locating the throw site. Returns "" if no stack was captured.

func (*Error) StackString

func (e *Error) StackString() string

StackString symbolizes the stack captured at construction time. Kept as raw []uintptr on the struct (cheap to capture) and only symbolized lazily when the mapper actually logs it, rather than paying runtime.CallersFrames cost on every construction.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the wrapped cause to errors.Is/errors.As chains.

func (*Error) WithCause

func (e *Error) WithCause(err error) *Error

WithCause wraps the original error for logging and errors.Is/As chains.

func (*Error) WithDetails

func (e *Error) WithDetails(details any) *Error

WithDetails attaches optional structured metadata surfaced to the client in the details field.

func (*Error) WithFieldErrors

func (e *Error) WithFieldErrors(fe []FieldError) *Error

WithFieldErrors attaches field-level validation errors surfaced to the client in the errors[] array.

func (*Error) WithLogMessage

func (e *Error) WithLogMessage(msg string) *Error

WithLogMessage overrides the default internal-only detail message (see newWithSkip) that the mapper logs but never sends to the client.

type ErrorResponse

type ErrorResponse struct {
	Status  string       `json:"status"`
	Code    string       `json:"code"`
	Message string       `json:"message"`
	Errors  []FieldError `json:"errors,omitempty"`
	Details any          `json:"details,omitempty"`
	// TrackingID correlates a 5xx response with its server-side log line (a
	// bare UUID, no source markers). Set only for 5xx responses — 4xx
	// responses already tell the client exactly what was wrong.
	TrackingID string `json:"trackingId,omitempty"`
}

ErrorResponse is the standard error response shape mandated by APR-004: { status, code, message, errors[], details }. status is always "error"; code is a stable, machine-readable string from the error catalog (see codes.go). Details carries optional structured metadata whose shape is specific to the code (e.g. the resources referencing a secret that blocked its deletion) — it is not a substitute for errors[], which is reserved for field-level validation failures.

func NewErrorResponse

func NewErrorResponse(httpStatus int, title string, description ...string) ErrorResponse

NewErrorResponse builds a standard ErrorResponse for the given HTTP status. title is retained for call-site compatibility but is no longer surfaced in the response body; the catalog code is derived from httpStatus (see codeForStatus above), and description[0], when given, becomes the human-readable message. Handlers that need a specific catalog code should use NewErrorResponseWithCode instead.

func NewErrorResponseWithCode

func NewErrorResponseWithCode(code, message string) ErrorResponse

NewErrorResponseWithCode builds a standard ErrorResponse using an explicit catalog code (see codes.go), for handlers that know the precise failure reason rather than just the HTTP status.

type FieldError

type FieldError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
}

FieldError describes a single field-level validation failure.

Jump to

Keyboard shortcuts

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