Documentation
¶
Overview ¶
Package apierror defines the structured error types used across all services.
Every user-facing error is represented as an APIError, which carries both a public message (safe to return in HTTP responses) and an internal message (for logs and traces only). Errors are categorized by an ErrorCode (machine-readable, e.g. "validation_failed") and an ErrorType (broad category, e.g. "invalid_request_error").
The package provides constructor functions for common error scenarios (validation, auth, not-found, etc.) and handles mapping error codes to HTTP status codes via GetHTTPStatusCode. For gRPC transport, errors can be serialized to/from JSON using ToJSON/APIErrorFromJSON.
Index ¶
- func GetHTTPStatusCode(code ErrorCode) int
- func Is5XXErrorCode(code ErrorCode) bool
- func IsNotFound(err *APIError) bool
- func IsTransientError(code ErrorCode, errorType ErrorType) bool
- type APIError
- func APIErrorFromJSON(jsonData []byte) (*APIError, error)
- func NewAPIError(code ErrorCode, errorType ErrorType, publicMessage string, ...) *APIError
- func NewAPIVersionInvalidError(version string, supported []string) *APIError
- func NewAPIVersionRequiredError() *APIError
- func NewAPIVersionTooOldError(requested, minimum string) *APIError
- func NewAgentSpendingCapReachedError(publicMessage string) *APIError
- func NewAlreadyDeletedError(publicMessage string) *APIError
- func NewAuthenticationError(publicMessage string) *APIError
- func NewAuthorizationError(publicMessage string) *APIError
- func NewClientClosedRequestError(publicMessage string) *APIError
- func NewConflictErrorWithParam(publicMessage string, param string) *APIError
- func NewExpiredAPIKeyError(publicMessage string) *APIError
- func NewExpiredTokenError(publicMessage string) *APIError
- func NewIdempotencyHashMismatchError(idempotencyKey string) *APIError
- func NewIdempotencyInProgressError(idempotencyKey string) *APIError
- func NewInternalError(internal error, internalMessage string) *APIError
- func NewInvalidFormatError(publicMessage string, param string) *APIError
- func NewInvariantViolationError(internalMessage string) *APIError
- func NewLimitExceededError(publicMessage string) *APIError
- func NewMethodNotAllowedError(publicMessage string) *APIError
- func NewMissingFieldError(publicMessage string, param string) *APIError
- func NewParameterInvalidError(publicMessage string, param string) *APIError
- func NewParameterMissingError(publicMessage string, param string) *APIError
- func NewParameterUnknownError(publicMessage string, param string) *APIError
- func NewPaymentRequiredError(publicMessage string) *APIError
- func NewRateLimitExceededError(publicMessage string) *APIError
- func NewRegistrationClosedError(publicMessage string) *APIError
- func NewRequestTimeoutError(internalMessage string) *APIError
- func NewResourceConflictError(publicMessage string) *APIError
- func NewResourceExistsError(publicMessage string) *APIError
- func NewResourceNotFoundError(publicMessage string) *APIError
- func NewRevokedAPIKeyError(publicMessage string) *APIError
- func NewValidationError(publicMessage string) *APIError
- func NewValidationErrorWithParam(publicMessage string, param string) *APIError
- func (e *APIError) Error() string
- func (e *APIError) ToJSON() ([]byte, error)
- func (e *APIError) ToResponseError() ResponseError
- func (e *APIError) ToResponseMap() any
- func (e *APIError) Unwrap() error
- func (e *APIError) WithDocURL(url string) *APIError
- func (e *APIError) WithInternal(err error) *APIError
- func (e *APIError) WithParam(param string) *APIError
- func (e *APIError) WithQuota(limit, used int32, resetAt *time.Time) *APIError
- type APIErrorOption
- type APIErrorResponse
- type ErrorCode
- type ErrorType
- type QuotaInfo
- type ResponseError
- type RowError
- type RowErrors
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GetHTTPStatusCode ¶
GetHTTPStatusCode maps an ErrorCode to the appropriate HTTP status code. Used by the API gateway to set the response status when writing error responses.
func Is5XXErrorCode ¶
Is5XXErrorCode returns true if the error code maps to a 5xx HTTP status.
func IsNotFound ¶
IsNotFound is a convenience check for whether an APIError is a resource_not_found error.
func IsTransientError ¶
IsTransientError returns true if the error code and type combination represents a temporary condition that may resolve on retry. The error type provides context that the code alone cannot — for example, a resource_conflict with ErrorTypeInvalidRequest (e.g. "username already taken") is not retryable, while server-side errors generally are.
Transient combinations:
- ErrorTypeAPI: all server errors except client_closed_request
- ErrorTypeIdempotency: only idempotency_in_progress (concurrent duplicate request)
- ErrorTypeInvalidRequest: only rate_limit_exceeded (back off and retry)
Types ¶
type APIError ¶
type APIError struct {
// Code is the machine-readable error code included in API responses.
Code ErrorCode `json:"code"`
// Type is the broad error category included in API responses.
Type ErrorType `json:"type"`
// PublicMessage is the human-readable message returned to the client.
PublicMessage string `json:"message"`
// Param is the request parameter that caused the error (e.g. "email"), if applicable.
Param string `json:"param,omitempty"`
// DocURL links to documentation about this specific error, if available.
DocURL string `json:"doc_url,omitempty"`
// IsTransient indicates whether the client should retry the request. Automatically set by NewAPIError based on the error code and type.
IsTransient bool `json:"is_transient"`
// Quota provides machine-readable details about a plan-imposed resource limit. Only populated for limit_exceeded errors. Included in API responses when non-nil.
Quota *QuotaInfo `json:"quota,omitempty"`
// InternalMessage is a developer-facing message for logs and traces. Never sent to clients.
InternalMessage string `json:"-"`
// Internal is the underlying error, if any. Never sent to clients. Accessible via Unwrap().
Internal error `json:"-"`
// Stack is the goroutine stack captured at the point a 5xx error is created, so the recorded trace points at the failing code rather than the response-writing layer. Empty for non-5xx errors. Never sent to clients. Propagated across gRPC via ToJSON.
Stack string `json:"-"`
}
APIError is the canonical error type used throughout the platform. It implements the error interface and separates public-facing information (Code, Type, PublicMessage) from internal diagnostics (InternalMessage, Internal) that are never exposed to clients.
Create instances using the constructor functions (NewValidationError, NewInternalError, etc.) rather than building APIError literals directly.
func APIErrorFromJSON ¶
APIErrorFromJSON reconstructs an APIError from JSON bytes produced by ToJSON. Used on the receiving side of gRPC calls to restore the full error with internal details.
func NewAPIError ¶
func NewAPIError(code ErrorCode, errorType ErrorType, publicMessage string, internalMessage string, opts ...APIErrorOption) *APIError
NewAPIError is the base constructor for all API errors. It sets IsTransient automatically based on the error code. Prefer the domain-specific constructors (NewValidationError, NewInternalError, etc.) for common cases; use this directly only when none of those fit.
func NewAPIVersionInvalidError ¶
NewAPIVersionInvalidError creates a 400 error when the requested API version is not recognized.
func NewAPIVersionRequiredError ¶
func NewAPIVersionRequiredError() *APIError
NewAPIVersionRequiredError creates a 400 error when the OpenMRP-Version header is missing.
func NewAPIVersionTooOldError ¶
NewAPIVersionTooOldError creates a 400 error when the requested API version is below the minimum version required by the endpoint.
func NewAgentSpendingCapReachedError ¶
NewAgentSpendingCapReachedError creates a 402 Payment Required error when an agent run is refused because the account has reached its monthly agent spending cap. Clients should prompt the user to raise or remove the cap.
func NewAlreadyDeletedError ¶
NewAlreadyDeletedError creates a 410 Gone error for resources that were already deleted.
func NewAuthenticationError ¶
NewAuthenticationError creates a 401 Unauthorized error for invalid credentials.
func NewAuthorizationError ¶
NewAuthorizationError creates a 403 Forbidden error when the caller lacks required permissions.
func NewClientClosedRequestError ¶
NewClientClosedRequestError creates a 499 error (nginx convention) when the client disconnects before the server finishes processing.
func NewConflictErrorWithParam ¶
NewConflictErrorWithParam creates a 409 Conflict error tied to a specific parameter (e.g. a duplicate email address).
func NewExpiredAPIKeyError ¶
NewExpiredAPIKeyError creates a 401 error when an API key has passed its expiration date.
func NewExpiredTokenError ¶
NewExpiredTokenError creates a 401 error when a JWT access token or refresh token has passed its expiration date. Distinct from NewAuthenticationError so that routine token-rotation traffic (a short-lived access token expiring, the client silently refreshing) can be told apart from genuine bad credentials.
func NewIdempotencyHashMismatchError ¶
NewIdempotencyHashMismatchError creates a validation error when an idempotency key is reused with different request parameters than the original request.
func NewIdempotencyInProgressError ¶
NewIdempotencyInProgressError creates a 409 Conflict error when a request with the same idempotency key is already being processed concurrently.
func NewInternalError ¶
NewInternalError creates a 500 Internal Server Error with a generic public message ("Something went wrong.") and wraps the underlying error for internal logging. If the underlying error is itself an APIError, their internal messages are chained.
func NewInvalidFormatError ¶
NewInvalidFormatError creates a 400 error when a field value does not match the expected format.
func NewInvariantViolationError ¶
NewInvariantViolationError creates a 500 error for conditions that should never occur in correct code (e.g. missing required data after a successful query). The public message is generic; the internal message captures what invariant was violated.
func NewLimitExceededError ¶
NewLimitExceededError creates a 403 Forbidden error when an account has reached a plan-imposed resource limit (e.g. maximum sandbox accounts).
func NewMethodNotAllowedError ¶
NewMethodNotAllowedError creates a 405 Method Not Allowed error.
func NewMissingFieldError ¶
NewMissingFieldError creates a 400 error when a required field is not provided in the request body.
func NewParameterInvalidError ¶
NewParameterInvalidError creates a 400 error when a query or path parameter value is not valid.
func NewParameterMissingError ¶
NewParameterMissingError creates a 400 error when a required query or path parameter is not provided.
func NewParameterUnknownError ¶
NewParameterUnknownError creates a 400 error when an unrecognized parameter is sent in the request.
func NewPaymentRequiredError ¶
NewPaymentRequiredError creates a 402 Payment Required error when the account's subscription is in a non-active state and must be resolved before continuing.
func NewRateLimitExceededError ¶
NewRateLimitExceededError creates a 429 Too Many Requests error. Marked as transient.
func NewRegistrationClosedError ¶
NewRegistrationClosedError creates a 403 Forbidden error when public registration for a plan code has reached its capacity.
func NewRequestTimeoutError ¶
NewRequestTimeoutError creates a 504 Gateway Timeout error. The deadline was exceeded on our side, not the client's, so this is a server failure rather than a 408. Marked as transient.
func NewResourceConflictError ¶
NewResourceConflictError creates a 409 Conflict error for state conflicts (e.g. concurrent updates).
func NewResourceExistsError ¶
NewResourceExistsError creates a 409 Conflict error for duplicate resource creation (e.g. unique constraint violation). Uses ErrorCodeResourceExists rather than ErrorCodeResourceConflict because the semantics are "this resource already exists" rather than a generic state conflict.
func NewResourceNotFoundError ¶
NewResourceNotFoundError creates a 404 Not Found error for missing resources.
func NewRevokedAPIKeyError ¶
NewRevokedAPIKeyError creates a 401 error when an API key has been explicitly revoked.
func NewValidationError ¶
NewValidationError creates a 400 Bad Request error for general input validation failures.
func NewValidationErrorWithParam ¶
NewValidationErrorWithParam creates a 400 Bad Request error tied to a specific request parameter.
func (*APIError) Error ¶
Error returns the internal (non-public) error string for logging. If an underlying error is wrapped, it is appended to the internal message.
func (*APIError) ToJSON ¶
ToJSON serializes the full APIError (including internal fields) to JSON for transport over gRPC. Use APIErrorFromJSON on the receiving side to reconstruct it.
func (*APIError) ToResponseError ¶
func (e *APIError) ToResponseError() ResponseError
renders the public fields as the canonical client-facing error object, so one embedded in another payload (e.g. a job's per-row failures) reads the same as any error envelope
func (*APIError) ToResponseMap ¶
ToResponseMap converts the APIError into an APIErrorResponse suitable for JSON serialization in HTTP responses. Only public fields are included.
func (*APIError) WithDocURL ¶
WithDocURL sets the documentation URL on the error and returns the same pointer for chaining.
func (*APIError) WithInternal ¶
WithInternal wraps an underlying error and returns the same pointer for chaining.
func (*APIError) WithParam ¶
WithParam sets the parameter name on the error and returns the same pointer for chaining.
type APIErrorOption ¶
type APIErrorOption func(*APIError)
APIErrorOption is a functional option applied during NewAPIError construction. Use the package-level WithParam, WithDocURL, and WithInternal functions to create options.
func WithDocURL ¶
func WithDocURL(url string) APIErrorOption
WithDocURL returns an option that sets the documentation URL on the error.
func WithInternal ¶
func WithInternal(err error) APIErrorOption
WithInternal returns an option that wraps an underlying error for logging/tracing.
func WithParam ¶
func WithParam(param string) APIErrorOption
WithParam returns an option that sets the offending parameter name on the error.
type APIErrorResponse ¶
type APIErrorResponse struct {
// The error object containing details about what went wrong.
Error ResponseError `json:"error"`
}
APIErrorResponse is the top-level envelope wrapping a ResponseError. All API error responses use this shape: { "error": { ... } }.
func (APIErrorResponse) SchemaExample ¶
func (r APIErrorResponse) SchemaExample() any
SchemaExample returns a representative instance for OpenAPI documentation generation.
type ErrorCode ¶
type ErrorCode string
ErrorCode is a machine-readable identifier for a specific error condition. These codes are returned in API responses and used by clients to programmatically handle errors without parsing human-readable messages.
const ( // ErrorCodeExpiredToken indicates a JWT access or refresh token has expired. ErrorCodeExpiredToken ErrorCode = "expired_token" // ErrorCodeExpiredAPIKey indicates an API key has passed its expiration date. ErrorCodeExpiredAPIKey ErrorCode = "api_key_expired" // #nosec G101 - This is an error code constant, not a hardcoded credential // ErrorCodeRevokedAPIKey indicates an API key was explicitly revoked by the owner or OpenMRP. ErrorCodeRevokedAPIKey ErrorCode = "api_key_revoked" // #nosec G101 - This is an error code constant, not a hardcoded credential // ErrorCodeInvalidCredentials indicates the provided credentials are wrong. ErrorCodeInvalidCredentials ErrorCode = "invalid_credentials" // #nosec G101 - This is an error code constant, not a hardcoded credential // ErrorCodeInsufficientPerms indicates the caller is authenticated but lacks the required role or permission. ErrorCodeInsufficientPerms ErrorCode = "insufficient_permissions" // ErrorCodePaymentRequired indicates the account's subscription is in a non-active state (past_due, canceled, unpaid) and must be resolved before the account can continue using the platform. ErrorCodePaymentRequired ErrorCode = "payment_required" // ErrorCodeAgentSpendingCapReached indicates the account has reached its configured monthly agent (LLM) spending cap, so the run was refused. The caller must raise or remove the cap to continue running agents. ErrorCodeAgentSpendingCapReached ErrorCode = "agent_spending_cap_reached" // ErrorCodeValidationFailed is a general validation failure indicating that the request is invalid. ErrorCodeValidationFailed ErrorCode = "validation_failed" // ErrorCodeMissingField indicates a required field was not provided in the request body. ErrorCodeMissingField ErrorCode = "missing_field" // ErrorCodeInvalidFormat indicates a field value does not match the expected format (e.g. invalid email). ErrorCodeInvalidFormat ErrorCode = "invalid_format" // ErrorCodeMethodNotAllowed indicates the HTTP method is not supported for this endpoint. ErrorCodeMethodNotAllowed ErrorCode = "method_not_allowed" // ErrorCodeResourceNotFound indicates the requested resource does not exist. ErrorCodeResourceNotFound ErrorCode = "resource_not_found" // ErrorCodeResourceExists indicates a resource with the same unique constraint already exists. ErrorCodeResourceExists ErrorCode = "resource_exists" // ErrorCodeResourceConflict indicates a state conflict that prevents the operation (e.g. duplicate username, optimistic locking failure). ErrorCodeResourceConflict ErrorCode = "resource_conflict" // ErrorCodeResourceGone indicates the resource existed but has been permanently deleted. ErrorCodeResourceGone ErrorCode = "resource_gone" // ErrorCodeIdempotencyInProgress indicates a request with the same idempotency key is currently being processed. The client should retry after a short delay. ErrorCodeIdempotencyInProgress ErrorCode = "idempotency_in_progress" // ErrorCodeLimitExceeded indicates the account has reached a plan-imposed resource limit (e.g. maximum sandbox accounts). The caller must upgrade their plan to increase the limit. ErrorCodeLimitExceeded ErrorCode = "limit_exceeded" // ErrorCodeRegistrationClosed indicates that public registration for the requested plan code has reached its capacity. Distinct from limit_exceeded, which applies to per-account resource quotas (e.g. sandbox count). ErrorCodeRegistrationClosed ErrorCode = "registration_closed" // ErrorCodeRateLimitExceeded indicates the caller has exceeded the allowed request rate. The client should back off and retry. ErrorCodeRateLimitExceeded ErrorCode = "rate_limit_exceeded" // ErrorCodeParameterMissing indicates a required query or path parameter was not provided. ErrorCodeParameterMissing ErrorCode = "parameter_missing" // ErrorCodeParameterInvalid indicates a parameter value is not valid (e.g. negative limit). ErrorCodeParameterInvalid ErrorCode = "parameter_invalid" // ErrorCodeParameterUnknown indicates an unrecognized parameter was sent in the request. ErrorCodeParameterUnknown ErrorCode = "parameter_unknown" // ErrorCodeParametersExclusive indicates two mutually exclusive parameters were both provided. ErrorCodeParametersExclusive ErrorCode = "parameters_exclusive" // ErrorCodeInternalError is an unexpected server-side failure. Transient; safe to retry. ErrorCodeInternalError ErrorCode = "internal_error" ErrorCodeSvcUnavailable ErrorCode = "service_unavailable" // ErrorCodeExternalSvcError indicates a third-party dependency returned an error. Transient. ErrorCodeExternalSvcError ErrorCode = "external_service_error" // ErrorCodeTimeout indicates the operation exceeded its deadline. Transient. ErrorCodeTimeout ErrorCode = "timeout" // ErrorCodeConnectionError indicates a network-level failure to a downstream service. Transient. ErrorCodeConnectionError ErrorCode = "connection_error" // ErrorCodeRequestTimeout indicates the overall request exceeded its deadline. Transient. ErrorCodeRequestTimeout ErrorCode = "request_timeout" // ErrorCodeClientClosedRequest indicates the client disconnected before the server finished. Not transient — the server cannot meaningfully retry on behalf of a disconnected client. ErrorCodeClientClosedRequest ErrorCode = "client_closed_request" // ErrorCodeAPIVersionRequired indicates the OpenMRP-Version header was missing from the request. ErrorCodeAPIVersionRequired ErrorCode = "api_version_required" // ErrorCodeAPIVersionInvalid indicates the requested API version string is not recognized. ErrorCodeAPIVersionInvalid ErrorCode = "api_version_invalid" // ErrorCodeAPIVersionTooOld indicates the requested API version is below the minimum supported by the endpoint. ErrorCodeAPIVersionTooOld ErrorCode = "api_version_too_old" )
func (ErrorCode) EnumValues ¶
EnumValues returns all valid ErrorCode values for use in schema generation.
type ErrorType ¶
type ErrorType string
ErrorType categorizes errors into broad classes for client-side handling.
- ErrorTypeAPI: server-side failures (5xx-class), safe to retry if transient.
- ErrorTypeIdempotency: idempotency key conflicts (concurrent or mismatched requests).
- ErrorTypeInvalidRequest: client-side mistakes (bad input, missing auth, not found).
const ( // ErrorTypeAPI covers server-side failures (5xx). Generally transient and safe to retry. ErrorTypeAPI ErrorType = "api_error" // ErrorTypeIdempotency covers idempotency key conflicts. May be transient (concurrent duplicate request) or permanent (reused key with different parameters). ErrorTypeIdempotency ErrorType = "idempotency_error" // ErrorTypeInvalidRequest covers client-side mistakes (bad input, missing auth, not found). Generally not retryable — the client must fix the request before resending. ErrorTypeInvalidRequest ErrorType = "invalid_request_error" )
func (ErrorType) EnumValues ¶
EnumValues returns all valid ErrorType values for use in schema generation.
type QuotaInfo ¶
type QuotaInfo struct {
// Limit is the maximum number of resources allowed by the current plan.
Limit int32 `json:"limit"`
// Used is the number of resources currently consumed.
Used int32 `json:"used"`
// ResetAt is the time when the quota resets, if applicable. Nil for static (non-metered) limits.
ResetAt *time.Time `json:"reset_at"`
}
QuotaInfo provides machine-readable details about a plan-imposed resource limit. Included in limit_exceeded errors so clients can display upgrade prompts, usage bars, or implement programmatic retry/backoff logic.
type ResponseError ¶
type ResponseError struct {
// A machine-readable code for the error.
Code ErrorCode `json:"code"`
// The type of error.
Type ErrorType `json:"type"`
// A human-readable message providing more details about the error.
Message string `json:"message"`
// The parameter that caused the error, if applicable.
Param *string `json:"param"`
// A URL to documentation about the error.
DocURL *string `json:"doc_url"`
// Whether this error is transient and the request can be retried.
IsTransient bool `json:"is_transient"`
// Quota provides plan limit details when the error is limit_exceeded. Nil otherwise.
Quota *QuotaInfo `json:"quota"`
// RequestLogURL is a link to the dashboard page for this request's log entry. Nil when no request log is available.
RequestLogURL *string `json:"request_log_url"`
}
ResponseError is the JSON-serializable error body returned to API clients. It contains only public information. This struct is used by the OpenAPI schema generator to produce documentation.
func (ResponseError) SchemaExample ¶
func (r ResponseError) SchemaExample() any
SchemaExample returns a representative instance for OpenAPI documentation generation.
type RowError ¶
type RowError struct {
// Zero-based row of the request this failure names.
Index int
// What went wrong — the same error object a synchronous error response carries.
Error ResponseError
}
pairs one row of a bulk request with the failure it produced. Internal to the services that run bulk work: the API reports a row's failure on the job result for that row, so this never reaches the wire.
func NewRowError ¶
records a row that failed, carrying the canonical client-facing error object
type RowErrors ¶
type RowErrors struct {
// contains filtered or unexported fields
}
collects the failures found across a bulk request's rows, keeping each one whole
func (*RowErrors) AddValidation ¶
records a row's field-level validation failure against a row-indexed param