ObjectStackObjectStack

Errors schema — API Protocol reference

Provides consistent, machine-readable error codes across the platform.

Standardized Error Codes Protocol

Implements P0 requirement for ObjectStack kernel. Provides consistent, machine-readable error codes across the platform.

Features:

  • Categorized error codes (validation, authentication, authorization, etc.)
  • HTTP status code mapping
  • Localization support
  • Retry guidance

Industry alignment: Google Cloud Errors, AWS Error Codes, Stripe API Errors

Source: packages/spec/src/api/errors.zod.ts

TypeScript Usage

import { EnhancedApiErrorSchema, ErrorCategory, ErrorResponseSchema, FieldErrorSchema, FieldErrorCode, RetryStrategy, StandardErrorCode } from '@objectstack/spec/api';
import type { EnhancedApiError, ErrorCategory, ErrorResponse, FieldError, FieldErrorCode, RetryStrategy, StandardErrorCode } from '@objectstack/spec/api';

// Validate data
const result = EnhancedApiErrorSchema.parse(data);

EnhancedApiError

Properties

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | 'INVALID_FORMAT' | 'VALUE_TOO_LONG' | 'VALUE_TOO_SHORT' | 'VALUE_OUT_OF_RANGE' | … +42 more>✅Machine-readable error code
messagestring✅Human-readable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim — see ApiErrorSchema.userMessage. Present only when the producer opted in at throw time; unmarked errors keep the generic consumer substitution.
refusaltrueoptionalProducer-declared deliberate refusal — see ApiErrorSchema.refusal. Present only when the producer declared it at throw time; a declared 5xx without it is a fault whose message is withheld. Until the withhold arms read the declaration, a declared refusal is still withheld.
categoryEnum<'validation' | 'authentication' | 'authorization' | 'not_found' | 'conflict' | 'rate_limit' | 'server' | 'external' | 'maintenance'>optionalError category
httpStatusnumberoptionalHTTP status code
retryablebooleanoptional (default: false)Whether the request can be retried
retryStrategyEnum<'no_retry' | 'retry_immediate' | 'retry_backoff' | 'retry_after'>optionalRecommended retry strategy
retryAfterSecondsnumberoptionalSeconds to wait before retrying
retryAfterneveroptional[REMOVED] EnhancedApiError.retryAfter was renamed to retryAfterSeconds in @objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Rename the key to retryAfterSeconds; the value (seconds) is unchanged. This is the ADR-0112 error envelope, not the HTTP Retry-After response header — that header keeps its RFC 9110 name and is untouched.
detailsanyoptionalAdditional error context
fields{ field: string; code: Enum<'required' | 'invalid_type' | 'invalid_shape' | 'unknown_field' | …>; message: string; label?: string; … }[]optionalOne entry per offending value
fieldErrorsneveroptional[REMOVED] EnhancedApiError.fieldErrors was renamed to fields in @objectstack/spec 17 (ADR-0114 D4) — the array is unchanged, only the property name. Every producer already emitted fields; fieldErrors was declared and never emitted, so a reader keying on it was reading a field no server sent.
timestampstringoptionalWhen the error occurred
requestIdstringoptionalRequest ID for tracking
traceIdstringoptionalDistributed trace ID
documentationstringoptionalURL to error documentation
helpTextstringoptionalSuggested actions to resolve the error

Allowed Values: EnhancedApiError.code

  • VALIDATION_ERROR
  • INVALID_FIELD
  • MISSING_REQUIRED_FIELD
  • INVALID_FORMAT
  • VALUE_TOO_LONG
  • VALUE_TOO_SHORT
  • VALUE_OUT_OF_RANGE
  • INVALID_REFERENCE
  • DUPLICATE_VALUE
  • INVALID_QUERY
  • INVALID_FILTER
  • INVALID_SORT
  • MAX_RECORDS_EXCEEDED
  • UNAUTHENTICATED
  • INVALID_CREDENTIALS
  • EXPIRED_TOKEN
  • INVALID_TOKEN
  • SESSION_EXPIRED
  • MFA_REQUIRED
  • EMAIL_NOT_VERIFIED
  • PERMISSION_DENIED
  • INSUFFICIENT_PRIVILEGES
  • FIELD_NOT_ACCESSIBLE
  • RECORD_NOT_ACCESSIBLE
  • LICENSE_REQUIRED
  • IP_RESTRICTED
  • TIME_RESTRICTED
  • RESOURCE_NOT_FOUND
  • OBJECT_NOT_FOUND
  • RECORD_NOT_FOUND
  • FIELD_NOT_FOUND
  • ENDPOINT_NOT_FOUND
  • RESOURCE_CONFLICT
  • CONCURRENT_MODIFICATION
  • DELETE_RESTRICTED
  • DUPLICATE_RECORD
  • LOCK_CONFLICT
  • METHOD_NOT_ALLOWED
  • PRECONDITION_REQUIRED
  • RATE_LIMIT_EXCEEDED
  • QUOTA_EXCEEDED
  • INTERNAL_ERROR
  • DATABASE_ERROR
  • TIMEOUT
  • SERVICE_UNAVAILABLE
  • NOT_IMPLEMENTED
  • EXTERNAL_SERVICE_ERROR
  • INTEGRATION_ERROR
  • WEBHOOK_DELIVERY_FAILED

Nested Shape: EnhancedApiError.fields[number]

PropertyTypeRequiredDescription
fieldstring✅Field path (supports dot notation)
codeEnum<'required' | 'invalid_type' | 'invalid_shape' | 'unknown_field' | …>✅Which constraint the value violated (field-level catalog, ADR-0114)
messagestring✅Human-readable error message, rendered in the caller’s locale
labelstringoptionalField display label in the caller’s locale
valueanyoptionalThe invalid value that was provided
constraintRecord<string, any>optionalThe constraint that was violated, as discrete values (e.g. { maxLength: 512, actual: 3000 })

ErrorCategory

Allowed Values

  • validation
  • authentication
  • authorization
  • not_found
  • conflict
  • rate_limit
  • server
  • external
  • maintenance

ErrorResponse

Properties

PropertyTypeRequiredDescription
successfalse✅Always false for error responses
error{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; message: string; userMessage?: string; refusal?: true; … }✅Error details
meta{ timestamp?: string; requestId?: string; traceId?: string }optionalResponse metadata

Nested Shape: ErrorResponse.error

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>✅Machine-readable error code
messagestring✅Human-readable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim — see ApiErrorSchema.userMessage. Present only when the producer opted in at throw time; unmarked errors keep the generic consumer substitution.
refusaltrueoptionalProducer-declared deliberate refusal — see ApiErrorSchema.refusal. Present only when the producer declared it at throw time; a declared 5xx without it is a fault whose message is withheld. Until the withhold arms read the declaration, a declared refusal is still withheld.
categoryEnum<'validation' | 'authentication' | 'authorization' | 'not_found' | 'conflict' | …>optionalError category
httpStatusnumberoptionalHTTP status code
retryablebooleanoptional (default: false)Whether the request can be retried
retryStrategyEnum<'no_retry' | 'retry_immediate' | 'retry_backoff' | 'retry_after'>optionalRecommended retry strategy
retryAfterSecondsnumberoptionalSeconds to wait before retrying
retryAfterneveroptional[REMOVED] EnhancedApiError.retryAfter was renamed to retryAfterSeconds in @objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Rename the key to retryAfterSeconds; the value (seconds) is unchanged. This is the ADR-0112 error envelope, not the HTTP Retry-After response header — that header keeps its RFC 9110 name and is untouched.
detailsanyoptionalAdditional error context
fields{ field: string; code: Enum<'required' | 'invalid_type' | 'invalid_shape' | 'unknown_field' | …>; message: string; label?: string; … }[]optionalOne entry per offending value
fieldErrorsneveroptional[REMOVED] EnhancedApiError.fieldErrors was renamed to fields in @objectstack/spec 17 (ADR-0114 D4) — the array is unchanged, only the property name. Every producer already emitted fields; fieldErrors was declared and never emitted, so a reader keying on it was reading a field no server sent.
timestampstringoptionalWhen the error occurred
requestIdstringoptionalRequest ID for tracking
traceIdstringoptionalDistributed trace ID
documentationstringoptionalURL to error documentation
helpTextstringoptionalSuggested actions to resolve the error

FieldError

Properties

PropertyTypeRequiredDescription
fieldstring✅Field path (supports dot notation)
codeEnum<'required' | 'invalid_type' | 'invalid_shape' | 'unknown_field' | 'invalid_boolean' | 'invalid_number' | 'invalid_date' | 'invalid_time' | 'invalid_email' | … +21 more>✅Which constraint the value violated (field-level catalog, ADR-0114)
messagestring✅Human-readable error message, rendered in the caller’s locale
labelstringoptionalField display label in the caller’s locale
valueanyoptionalThe invalid value that was provided
constraintRecord<string, any>optionalThe constraint that was violated, as discrete values (e.g. { maxLength: 512, actual: 3000 })

Allowed Values: FieldError.code

  • required
  • invalid_type
  • invalid_shape
  • unknown_field
  • invalid_boolean
  • invalid_number
  • invalid_date
  • invalid_time
  • invalid_email
  • invalid_url
  • invalid_phone
  • invalid_json
  • invalid_format
  • min_length
  • max_length
  • min_value
  • max_value
  • max_scale
  • max_precision
  • min_items
  • max_items
  • invalid_option
  • value_domain
  • invalid_value
  • reference_not_found
  • reference_ambiguous
  • rule_violation
  • json_schema_violation
  • invalid_initial_state
  • invalid_transition

FieldErrorCode

Allowed Values

  • required
  • invalid_type
  • invalid_shape
  • unknown_field
  • invalid_boolean
  • invalid_number
  • invalid_date
  • invalid_time
  • invalid_email
  • invalid_url
  • invalid_phone
  • invalid_json
  • invalid_format
  • min_length
  • max_length
  • min_value
  • max_value
  • max_scale
  • max_precision
  • min_items
  • max_items
  • invalid_option
  • value_domain
  • invalid_value
  • reference_not_found
  • reference_ambiguous
  • rule_violation
  • json_schema_violation
  • invalid_initial_state
  • invalid_transition

RetryStrategy

Allowed Values

  • no_retry
  • retry_immediate
  • retry_backoff
  • retry_after

StandardErrorCode

Allowed Values

  • VALIDATION_ERROR
  • INVALID_FIELD
  • MISSING_REQUIRED_FIELD
  • INVALID_FORMAT
  • VALUE_TOO_LONG
  • VALUE_TOO_SHORT
  • VALUE_OUT_OF_RANGE
  • INVALID_REFERENCE
  • DUPLICATE_VALUE
  • INVALID_QUERY
  • INVALID_FILTER
  • INVALID_SORT
  • MAX_RECORDS_EXCEEDED
  • UNAUTHENTICATED
  • INVALID_CREDENTIALS
  • EXPIRED_TOKEN
  • INVALID_TOKEN
  • SESSION_EXPIRED
  • MFA_REQUIRED
  • EMAIL_NOT_VERIFIED
  • PERMISSION_DENIED
  • INSUFFICIENT_PRIVILEGES
  • FIELD_NOT_ACCESSIBLE
  • RECORD_NOT_ACCESSIBLE
  • LICENSE_REQUIRED
  • IP_RESTRICTED
  • TIME_RESTRICTED
  • RESOURCE_NOT_FOUND
  • OBJECT_NOT_FOUND
  • RECORD_NOT_FOUND
  • FIELD_NOT_FOUND
  • ENDPOINT_NOT_FOUND
  • RESOURCE_CONFLICT
  • CONCURRENT_MODIFICATION
  • DELETE_RESTRICTED
  • DUPLICATE_RECORD
  • LOCK_CONFLICT
  • METHOD_NOT_ALLOWED
  • PRECONDITION_REQUIRED
  • RATE_LIMIT_EXCEEDED
  • QUOTA_EXCEEDED
  • INTERNAL_ERROR
  • DATABASE_ERROR
  • TIMEOUT
  • SERVICE_UNAVAILABLE
  • NOT_IMPLEMENTED
  • EXTERNAL_SERVICE_ERROR
  • INTEGRATION_ERROR
  • WEBHOOK_DELIVERY_FAILED

On this page