Skip to content

Error Contract

Every error response across all channels follows the same contract. The transport determines where these fields appear — JSON body for HTTP/GraphQL, trailers for gRPC, diagnostic messages for LDAP — but the shape is identical.

For the full list of error codes and their details schemas, see the generated Error Catalog. For retry strategy and error correlation, see Error Handling.

FieldStabilityDescription
idStableUnique failure identifier (UUID)
timestampStableWhen the failure occurred (ISO 8601)
codeStableMachine-readable error code (e.g., ARGUMENT_INVALID_JSON)
kindStableError classification (e.g., INVALID_ARGUMENT)
messageInformationalHuman-readable description — may change between versions, do not parse
trace_idStableOpenTelemetry trace ID (optional, when tracing is active)
span_idStableOpenTelemetry span ID (optional)
correlationStableRequest correlation ID (optional, when client provides one)
retryStableRetry hint: { "after": "PT2S" } — absent when retry is not suggested
detailsStableStructured context specific to the error code (optional)

Backwards compatibility: details schemas may gain new optional fields, but existing fields are never removed or renamed.

Default codes: When no explicit code is set, the code defaults to the kind name (e.g., INVALID_ARGUMENT). See the Error Catalog for all codes.

Scribe uses 16 error kinds, aligned with gRPC status codes:

KindMeaningDefault Retry
CANCELLEDCaller cancelled the operationNever
INVALID_ARGUMENTBad request parametersNever
OUT_OF_RANGEValue out of rangeNever
FAILED_PRECONDITIONWrong system stateNever
UNAUTHENTICATEDMissing or invalid credentialsNever
PERMISSION_DENIEDCaller lacks permissionNever
NOT_FOUNDResource not foundNever
ALREADY_EXISTSResource already existsNever
CONFLICTConflicts with current stateNever
RESOURCE_EXHAUSTEDQuota or capacity exceededAfter 2s
DEADLINE_EXCEEDEDOperation timed outAfter 1s
UNAVAILABLEService temporarily unavailableAfter 5s
UNIMPLEMENTEDOperation not supportedNever
INTERNALInternal error (invariant broken)Never
DATA_LOSSUnrecoverable data corruptionNever
UNKNOWNUnknown or unmapped errorNever

Each channel carries the same error information in its native format:

ChannelWhere to find the error
HTTP/RESTJSON body under error + response headers (Error-Id, Error-Code, Retry-After)
GraphQLerrors[].extensions.error (same shape as HTTP)
gRPCgoogle.rpc.Status with ErrorInfo in details + metadata trailers
LDAPResultCode + diagnostic message with JSON {"code":"...","id":"..."}

REST example:

HTTP/1.1 503 Service Unavailable
Error-Id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
Error-Code: DIRECTORY_BUSY
Retry-After: 2
{
"error": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"code": "DIRECTORY_BUSY",
"kind": "UNAVAILABLE",
"message": "Directory service is busy. Please retry later.",
"status": 503,
"retry": { "after": "PT2S" }
}
}

For full transport details (status code mappings, header/trailer formats, gRPC ErrorInfo metadata), see the REST, GraphQL, and LDAP channel docs.

Note: Some details fields (configuredBases, baseTypes, value, filter) may contain sensitive information. Deployments can omit these fields depending on their exposure policy. Clients should handle missing details gracefully.