Documentation
¶
Overview ¶
Package errcode provides structured error codes for the GoCell framework. All errors exposed across package boundaries must use this package instead of bare errors.New. Error codes follow the ERR_{MODULE}_{REASON} convention.
Package errcode provides structured error codes for the GoCell framework. All errors exposed across package boundaries must use this package instead of bare errors.New.
Index ¶
- func IsDomainNotFound(err error, codes ...Code) bool
- func IsExpected4xx(err error) bool
- func IsInfraError(err error) bool
- func IsTransient(err error) bool
- type Category
- type Code
- type Error
- func New(code Code, message string) *Error
- func NewAuth(code Code, message string) *Error
- func NewDomain(code Code, message string) *Error
- func NewInfra(code Code, message string) *Error
- func Safe(code Code, publicMsg, internalMsg string) *Error
- func WithDetails(err *Error, details map[string]any) *Error
- func Wrap(code Code, message string, cause error) *Error
- func WrapAuth(code Code, message string, cause error) *Error
- func WrapDomain(code Code, message string, cause error) *Error
- func WrapInfra(code Code, message string, cause error) *Error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsDomainNotFound ¶
IsDomainNotFound reports whether err is a domain-layer not-found condition whose code is in the caller-supplied whitelist. Both conditions must hold:
- err must be an *Error with Category == CategoryDomain
- err.Code must appear in codes
This two-gated check prevents infra errors from ever matching, regardless of which code they carry — the dual-channel invariant from k8s IsNotFound.
Callers pass Code constants directly; no string(...) conversion is needed.
func IsExpected4xx ¶
IsExpected4xx reports whether err maps to an HTTP 400-499 response code. These are expected client-side / business rejection conditions that callers should log at Warn level; true infrastructure failures should be Error.
Returns false for nil and for unclassified / plain errors (which are infra).
func IsInfraError ¶
IsInfraError reports whether err represents an infrastructure failure.
Fail-closed semantics: any error that is not definitively classified as a domain / validation / auth error is treated as infra. This prevents infra outages from silently propagating into domain-not-found branches.
Returns false only for nil. Returns true for:
- context.Canceled / context.DeadlineExceeded
- driver.ErrBadConn / sql.ErrConnDone
- *Error with Category == CategoryInfra or CategoryUnspecified
- any unrecognised plain error (fail-closed)
Stdlib sentinel coverage is intentionally narrow (context.* / sql.Err* / driver.ErrBadConn). Adapters that return wrapped plain errors are covered by the fail-closed fallback: CategoryUnspecified → treated as infra. New adapters that return wrapped custom error types should construct them with NewInfra (or WrapInfra) so the category is explicit rather than relying on the fallback; no change to classify.go is required.
func IsTransient ¶
IsTransient reports whether err, or any error in its Unwrap chain, carries the ErrKeyProviderTransient code. It is the canonical predicate for routing KeyProvider failures in EventBus handlers:
if errcode.IsTransient(err) {
return outbox.HandleResult{Disposition: outbox.DispositionRequeue, Err: err}
}
return outbox.HandleResult{Disposition: outbox.DispositionReject, Err: err}
Transient conditions map to Vault HTTP 503 / 429 / 408 / 499 (sealed, rate-limited, request timeout). All other KeyProvider errors are permanent (400 / 403 / 404) and must be routed to DispositionReject → DLX.
Returns false for nil. Uses errors.As so it correctly traverses chains produced by fmt.Errorf("…: %w", err).
ref: aws/aws-encryption-sdk-python src/aws_encryption_sdk/exceptions.py
Types ¶
type Category ¶
type Category int
Category classifies the origin of an error for log-level routing and dual-channel triage (infra vs domain). The zero value CategoryUnspecified is treated as infra (fail-closed) by all classifiers.
ref: k8s apimachinery pkg/api/errors — IsNotFound dual-channel pattern (infra errors must never map to domain not-found)
const ( // CategoryUnspecified is the zero value. Classifiers treat it as infra // (fail-closed) to prevent leaking infra faults into domain branches. CategoryUnspecified Category = iota // CategoryDomain signals a well-known business-layer condition // (resource not found, conflict, validation failure). CategoryDomain // CategoryInfra signals an infrastructure failure (DB down, network // timeout, bad connection). Must never be mapped to domain not-found. CategoryInfra // CategoryValidation signals a caller input validation failure (400-class). CategoryValidation // CategoryAuth signals an authentication / authorisation failure (401/403). CategoryAuth )
type Code ¶
type Code string
Code is a typed error code string.
const ( ErrMetadataInvalid Code = "ERR_METADATA_INVALID" ErrMetadataNotFound Code = "ERR_METADATA_NOT_FOUND" ErrCellNotFound Code = "ERR_CELL_NOT_FOUND" ErrSliceNotFound Code = "ERR_SLICE_NOT_FOUND" ErrContractNotFound Code = "ERR_CONTRACT_NOT_FOUND" ErrAssemblyNotFound Code = "ERR_ASSEMBLY_NOT_FOUND" ErrLifecycleInvalid Code = "ERR_LIFECYCLE_INVALID" ErrDependencyCycle Code = "ERR_DEPENDENCY_CYCLE" ErrValidationFailed Code = "ERR_VALIDATION_FAILED" ErrReferenceBroken Code = "ERR_REFERENCE_BROKEN" ErrInternal Code = "ERR_INTERNAL" ErrAuthForbidden Code = "ERR_AUTH_FORBIDDEN" ErrRateLimited Code = "ERR_RATE_LIMITED" ErrCSRFOriginDenied Code = "ERR_CSRF_ORIGIN_DENIED" ErrBodyTooLarge Code = "ERR_BODY_TOO_LARGE" ErrJourneyNotFound Code = "ERR_JOURNEY_NOT_FOUND" ErrTestExecution Code = "ERR_TEST_EXECUTION" ErrCheckRefInvalid Code = "ERR_CHECKREF_INVALID" ErrZeroTestMatch Code = "ERR_ZERO_TEST_MATCH" ErrBusClosed Code = "ERR_BUS_CLOSED" ErrCellMissingOutbox Code = "ERR_CELL_MISSING_OUTBOX" ErrCellMissingCodec Code = "ERR_CELL_MISSING_CODEC" ErrCellMissingTokenIssuer Code = "ERR_CELL_MISSING_TOKEN_ISSUER" ErrCellInvalidConfig Code = "ERR_CELL_INVALID_CONFIG" ErrSessionNotFound Code = "ERR_SESSION_NOT_FOUND" ErrSessionConflict Code = "ERR_SESSION_CONFLICT" ErrOrderNotFound Code = "ERR_ORDER_NOT_FOUND" ErrDeviceNotFound Code = "ERR_DEVICE_NOT_FOUND" ErrCommandNotFound Code = "ERR_COMMAND_NOT_FOUND" ErrAdapterPGNoTx Code = "ERR_ADAPTER_PG_NO_TX" ErrAuthKeyInvalid Code = "ERR_AUTH_KEY_INVALID" // ErrAuthVerifierConfig signals a JWT verifier construction error — e.g. // required configuration (WithExpectedAudiences) was not provided. // Distinct from ErrAuthKeyInvalid (key material) so operators can route // verifier misconfiguration separately from cryptographic key failures. ErrAuthVerifierConfig Code = "ERR_AUTH_VERIFIER_CONFIG" ErrAuthTokenInvalid Code = "ERR_AUTH_TOKEN_INVALID" ErrAuthTokenExpired Code = "ERR_AUTH_TOKEN_EXPIRED" // ErrAuthInvalidTokenIntent signals that a JWT's token_use claim (and/or // its JOSE typ header) does not match the expected intent for the current // request scope — e.g., a refresh token presented at a business endpoint, // or an access token presented at /auth/refresh. Middleware and slice // layers map this to a generic ERR_AUTH_UNAUTHORIZED / ERR_AUTH_REFRESH_FAILED // response to prevent token-type enumeration; the specific code is only // visible in logs. // // ref: RFC 8725 §2.8 / §3.11 (JWT token confusion threat model) // ref: AWS Cognito token_use claim, Keycloak typ header constants ErrAuthInvalidTokenIntent Code = "ERR_AUTH_INVALID_TOKEN_INTENT" // Access-core cell error codes. ErrAuthUserNotFound Code = "ERR_AUTH_USER_NOT_FOUND" ErrAuthUserDuplicate Code = "ERR_AUTH_USER_DUPLICATE" ErrAuthRoleNotFound Code = "ERR_AUTH_ROLE_NOT_FOUND" ErrAuthRoleDuplicate Code = "ERR_AUTH_ROLE_DUPLICATE" ErrAuthInvalidInput Code = "ERR_AUTH_INVALID_INPUT" ErrAuthUserLocked Code = "ERR_AUTH_USER_LOCKED" ErrAuthSessionInvalidInput Code = "ERR_AUTH_SESSION_INVALID_INPUT" ErrAuthIdentityInvalidInput Code = "ERR_AUTH_IDENTITY_INVALID_INPUT" ErrAuthLoginInvalidInput Code = "ERR_AUTH_LOGIN_INVALID_INPUT" ErrAuthLoginFailed Code = "ERR_AUTH_LOGIN_FAILED" ErrAuthLogoutInvalidInput Code = "ERR_AUTH_LOGOUT_INVALID_INPUT" ErrAuthRefreshInvalidInput Code = "ERR_AUTH_REFRESH_INVALID_INPUT" ErrAuthRefreshFailed Code = "ERR_AUTH_REFRESH_FAILED" // Deprecated: use ErrRefreshTokenReused (CategoryAuth) via runtime/auth/refresh.ErrTokenReused. // This code is retained only for sessionrefresh.service's current implementation; // the F2 refresh store PR will migrate callers and remove this constant. ErrAuthRefreshTokenReuse Code = "ERR_AUTH_REFRESH_TOKEN_REUSE" ErrAuthInvalidToken Code = "ERR_AUTH_INVALID_TOKEN" ErrAuthRBACInvalidInput Code = "ERR_AUTH_RBAC_INVALID_INPUT" ErrAuthKeyMissing Code = "ERR_AUTH_KEY_MISSING" ErrAuthSelfDelete Code = "ERR_AUTH_SELF_DELETE" // ErrAuthPasswordResetRequired signals that the authenticated subject must // change their password before accessing business endpoints. The middleware // enforces this when the JWT claim password_reset_required is true. // Only the exempt endpoints (POST /api/v1/access/users/{id}/password and // DELETE /api/v1/access/sessions/{id}) bypass this check. ErrAuthPasswordResetRequired Code = "ERR_AUTH_PASSWORD_RESET_REQUIRED" // Config-core cell error codes. ErrConfigNotFound Code = "ERR_CONFIG_NOT_FOUND" ErrConfigDuplicate Code = "ERR_CONFIG_DUPLICATE" ErrConfigInvalidInput Code = "ERR_CONFIG_INVALID_INPUT" ErrConfigPublishInvalidInput Code = "ERR_CONFIG_PUBLISH_INVALID_INPUT" ErrConfigRepoNotFound Code = "ERR_CONFIG_REPO_NOT_FOUND" ErrConfigRepoDuplicate Code = "ERR_CONFIG_REPO_DUPLICATE" ErrConfigRepoQuery Code = "ERR_CONFIG_REPO_QUERY" ErrFlagNotFound Code = "ERR_FLAG_NOT_FOUND" ErrFlagDuplicate Code = "ERR_FLAG_DUPLICATE" ErrFlagInvalidInput Code = "ERR_FLAG_INVALID_INPUT" ErrFlagRepoQuery Code = "ERR_FLAG_REPO_QUERY" // Audit-core cell error codes. ErrAuditRepoNotFound Code = "ERR_AUDIT_REPO_NOT_FOUND" ErrAuditRepoQuery Code = "ERR_AUDIT_REPO_QUERY" ErrArchiveUpload Code = "ERR_ARCHIVE_UPLOAD" ErrArchiveMarshal Code = "ERR_ARCHIVE_MARSHAL" ErrNotImplemented Code = "ERR_NOT_IMPLEMENTED" // Pagination / validation error codes. ErrCursorInvalid Code = "ERR_CURSOR_INVALID" ErrPageSizeExceeded Code = "ERR_PAGE_SIZE_EXCEEDED" ErrInvalidTimeFormat Code = "ERR_INVALID_TIME_FORMAT" // Resilience middleware error codes. ErrCircuitOpen Code = "ERR_CIRCUIT_OPEN" // Outbox relay health error codes. // ErrRelayBudgetExhausted signals that an outbox relay operation (poll / // reclaim / cleanup) has exceeded its consecutive-failure threshold, tripping // the failure budget and marking /readyz unhealthy. ErrRelayBudgetExhausted Code = "ERR_RELAY_BUDGET_EXHAUSTED" // Observability configuration error. // Raised by kernel / runtime observability constructors when a // required dependency (Provider, cellID) is missing or malformed. // Semantically an initialisation error — distinct from // ErrValidationFailed (user-input validation) so operators can route // the two through different dashboards. ErrObservabilityConfigInvalid Code = "ERR_OBSERVABILITY_CONFIG_INVALID" // WebSocket runtime error codes. ErrWSConnNotFound Code = "ERR_WS_CONN_NOT_FOUND" ErrWSAlreadyStarted Code = "ERR_WS_ALREADY_STARTED" ErrWSAlreadyStopped Code = "ERR_WS_ALREADY_STOPPED" ErrWSHubStopping Code = "ERR_WS_HUB_STOPPING" ErrWSHubNotRunning Code = "ERR_WS_HUB_NOT_RUNNING" ErrWSMaxConns Code = "ERR_WS_MAX_CONNS" // Outbox envelope error codes. // ErrEnvelopeSchema signals that an inbound wire message does not conform // to the expected envelope schema — unknown schemaVersion, missing required // fields, or corrupt JSON. Consumers must Reject (not retry) on this error. ErrEnvelopeSchema Code = "ERR_ENVELOPE_SCHEMA" // Bootstrap lifecycle error codes. // ErrBootstrapLifecycle signals that a lifecycle operation was called in an // invalid state — e.g. Append or Start called after the lifecycle has already // started. Distinct from ErrLifecycleInvalid (metadata validation) so // operators can route runtime lifecycle faults separately. ErrBootstrapLifecycle Code = "ERR_BOOTSTRAP_LIFECYCLE" // Refresh token store error codes (runtime/auth/refresh). // These are returned by refresh.Store implementations; callers use // errors.Is against the package-level sentinels in refresh/errors.go. // // ErrRefreshTokenNotFound / ErrRefreshTokenExpired / ErrRefreshTokenRevoked // are CategoryDomain — expected client-observable conditions. // ErrRefreshTokenReused is CategoryAuth — an OAuth2 RFC 6749 §10.4 // attack signal that triggers cascade revocation. ErrRefreshTokenNotFound Code = "ERR_REFRESH_TOKEN_NOT_FOUND" ErrRefreshTokenExpired Code = "ERR_REFRESH_TOKEN_EXPIRED" ErrRefreshTokenRevoked Code = "ERR_REFRESH_TOKEN_REVOKED" ErrRefreshTokenReused Code = "ERR_REFRESH_TOKEN_REUSED" // KeyProvider error codes. // ErrKeyProviderKeyNotFound signals that the requested key ID is not // present in the provider's keyring — e.g. a historical key that has been // purged. Callers must not fall back to plaintext; surface as a config error. // Permanent error — EventBus handlers should return DispositionReject. // Maps to Vault HTTP 404 (key or mount not found). ErrKeyProviderKeyNotFound Code = "ERR_KEY_PROVIDER_KEY_NOT_FOUND" // ErrKeyProviderAuthFailed signals that the Vault token has been revoked, // has insufficient permissions, or has expired (Vault HTTP 403 Forbidden). // Distinct from ErrKeyProviderKeyNotFound (404 — key absent) so operators // can route permission/token failures separately from missing-key failures. // // Use when: // - Vault returns HTTP 403 on any transit read/encrypt/decrypt path. // - Token revoked (revoke-accessor) or token lacks required capabilities. // - Permission denied on transit/keys/{name} or transit/encrypt|decrypt. // // Permanent error — EventBus handlers should return DispositionReject. // Operators must rotate the Vault token (not retry the operation). ErrKeyProviderAuthFailed Code = "ERR_KEY_PROVIDER_AUTH_FAILED" // ErrKeyProviderEncryptFailed signals a KMS encrypt-side operation failure // (Vault Transit encrypt API error, malformed response, etc.). Distinct from // ErrKeyProviderDecryptFailed so callers and log aggregators can route // encrypt-side failures (usually transient / retriable) separately from // decrypt-side failures (usually permanent / data integrity signal). // Permanent error — EventBus handlers should return DispositionReject. ErrKeyProviderEncryptFailed Code = "ERR_KEY_PROVIDER_ENCRYPT_FAILED" // ErrKeyProviderDecryptFailed signals an AES-GCM authentication failure, // wrong key, or malformed ciphertext. Fail-closed: callers must surface // this as an error and never return raw ciphertext or empty string. // Permanent error — EventBus handlers should return DispositionReject. ErrKeyProviderDecryptFailed Code = "ERR_KEY_PROVIDER_DECRYPT_FAILED" // ErrKeyProviderRotateFailed signals a key-rotation operation failure // (Vault rotate API returned an error, new key version could not be read // back, malformed response). Distinct from ErrKeyProviderKeyNotFound so // rotation-path retries and alerting do not confuse "key absent" with // "rotation API unreachable". // Permanent error — EventBus handlers should return DispositionReject. ErrKeyProviderRotateFailed Code = "ERR_KEY_PROVIDER_ROTATE_FAILED" // ErrKeyProviderTransient signals a transient KeyProvider failure that is // safe to retry after back-off. Maps to Vault HTTP responses indicating // temporary unavailability: // // - 503 Service Unavailable (sealed, standby, maintenance) // - 429 Too Many Requests (rate-limited) // - 408 Request Timeout / network timeout // // Contrast with ErrKeyProviderEncryptFailed / ErrKeyProviderDecryptFailed / // ErrKeyProviderKeyNotFound / ErrKeyProviderRotateFailed, which signal // permanent conditions (400 Bad Request, 403 Forbidden, 404 Not Found). // // EventBus Disposition routing: // - ErrKeyProviderTransient → DispositionRequeue (back-off retry) // - All other KeyProvider errors → DispositionReject (DLX) // // Use IsTransient to check the full error chain. // // ref: aws/aws-encryption-sdk-python src/aws_encryption_sdk/exceptions.py // (GenerateKeyError / DecryptKeyError transient vs permanent split) ErrKeyProviderTransient Code = "ERR_KEY_PROVIDER_TRANSIENT" // ErrConfigDecryptFailed signals that a sensitive config value could not be // decrypted at the repository boundary. Maps to HTTP 500 (internal error). ErrConfigDecryptFailed Code = "ERR_CONFIG_DECRYPT_FAILED" // ErrConfigKeyMissing signals that a required encryption key (GOCELL_MASTER_KEY // or vault token) is absent at startup. Triggers fail-fast in postgres mode. ErrConfigKeyMissing Code = "ERR_CONFIG_KEY_MISSING" )
Sentinel error codes used throughout the GoCell framework.
type Error ¶
type Error struct {
Code Code
Message string
InternalMessage string
Details map[string]any
Cause error
Category Category
}
Error is a structured error that carries a machine-readable Code, a human-readable Message, optional Details, and an optional wrapped Cause.
InternalMessage holds diagnostic detail that must never be exposed to API consumers. When present, Error() uses it (for logs/traces); HTTP response writers use Message (safe for clients).
Category classifies the error origin for log-level routing and infra/domain triage. The zero value CategoryUnspecified is treated as infra (fail-closed). Use NewInfra / NewDomain constructors to set the appropriate category; the legacy New / Wrap / Safe constructors leave Category at its zero value to preserve backward compatibility.
func NewAuth ¶
NewAuth creates an *Error with CategoryAuth. Use this for authentication / authorisation failures (401/403) and attack signals such as refresh token reuse detection (OAuth2 RFC 6749 §10.4).
func NewDomain ¶
NewDomain creates an *Error with CategoryDomain. Use this for well-known business-layer conditions (resource missing, conflict, etc.) that callers may handle specifically.
func NewInfra ¶
NewInfra creates an *Error with CategoryInfra. Use this for storage, network, and dependency failures so they are never confused with domain not-found conditions.
func Safe ¶
Safe creates an *Error with separate public and internal messages. publicMsg is returned to API clients; internalMsg is used in logs/traces via Error() and must never be exposed over the wire.
func WithDetails ¶
WithDetails returns a shallow copy of err with the provided details merged in. If err.Details is nil a new map is allocated; existing keys are preserved unless overwritten by the supplied details. It panics if err is nil — callers must not pass a nil *Error.
func WrapAuth ¶
WrapAuth creates an *Error with CategoryAuth that wraps the supplied cause. Use this when an authentication / authorisation failure or attack signal (e.g. reuse detection per RFC 6749 §10.4) has an underlying cause to preserve for error chain inspection (errors.Is / errors.As / Unwrap). The cause is stored in Error.Cause and exposed via Error.Error() in logs.
func WrapDomain ¶
WrapDomain creates an *Error with CategoryDomain that wraps the supplied cause. Use this when a domain-layer condition has an underlying cause to preserve. The cause is stored in Error.Cause and exposed via Error.Error() in logs.
func WrapInfra ¶
WrapInfra creates an *Error with CategoryInfra that wraps the supplied cause. Use this when an infrastructure failure has an underlying cause that should be preserved for error chain inspection (errors.Is / errors.As / Unwrap). The cause is stored in Error.Cause and exposed via Error.Error() in logs.