Skip to content
Registry StackDocsv0.20.0

Error and status code reference

View as Markdown

Each of the three services answers a failing request with a fixed, value-free body. None of them attaches the rejected value, the SQL it would have run, a source path, token material, or a principal identifier to the failure. This page carries Relay’s set in full and points at the two sets that are written down elsewhere under their own drift gates.

Relay returns errors as RFC 9457 problem details with the media type application/problem+json. The set is closed: ProblemCode in crates/registry-relay-v2/src/problem.rs has 26 variants and the HTTP boundary can return nothing else.

Every problem body carries exactly six members, and no operation adds a seventh.

MemberValue
typehttps://id.registrystack.org/problems/registry-relay/<code with dots replaced by slashes>
titleThe fixed title for the code
statusThe HTTP status, repeated in the response status line
detailThe fixed detail sentence for the code
codeThe stable code string
traceIdThe 32-character W3C Trace Context trace identifier for this request

Every problem response also carries Content-Type: application/problem+json and Cache-Control: no-store. The two 401 codes add WWW-Authenticate: Bearer realm="registry-relay". The two 429 codes add Retry-After: 60.

Branch on code and status. Do not parse the type URI path: it exists to identify and document the problem type.

traceId comes from the request’s traceparent header when that header parses, and from a server-created context when it does not. Relay always removes a caller-supplied tracestate and never reflects it, so vendor state cannot round-trip through the error boundary.

Unlike a taxonomy that varies its status by operation, each Relay code maps to exactly one status.

CodeStatusTitleDetail
consultation.invalid_request400Consultation request is invalidthe consultation request is invalid
aggregate-data.invalid_request400Aggregate data request is invalidthe aggregate data request is invalid
request.fields_invalid400Field selection is invalidfield selection is invalid
filter.unknown_field400Filter is not declaredfilter is not declared for this operation
filter.invalid_value400Filter value is invalidfilter value is invalid
query.cursor_invalid400Cursor is invalidcursor is invalid for this query
request.access_profile_invalid400Access profile selection is invalidaccess profile selection is invalid
auth.missing_credential401Bearer access token is requireda bearer access token is required
auth.invalid_credential401Bearer access token is invalidbearer access token validation failed
consultation.denied403Consultation is not permittedthe consultation is not permitted
aggregate-data.denied403Aggregate data access is not permittedaggregate data access is not permitted
resource.not_found404Requested resource was not foundthe requested resource was not found
consultation.unresolved404Requested record was not resolvedthe requested record was not resolved
format.unsupported406Requested format is not supportedthe requested format is not supported
internal.payload_too_large413Request body is too largerequest body exceeds the configured limit
consultation.response_too_large413Consultation response is too largethe consultation response exceeds the configured limit
aggregate-data.too_large413Aggregate data request is too broadthe aggregate data request exceeds its observation limit
internal.uri_too_long414Request URI is too longrequest URI exceeds the configured limit
request.media_type_unsupported415Request media type is not supportedrequest body must use application/json
consultation.rate_limited429Consultation quota is exhaustedthe consultation quota is exhausted
aggregate-data.rate_limited429Aggregate data quota is exhaustedthe aggregate data quota is exhausted
internal.unhandled500Request could not be servedthe request could not be served
source.unavailable503Authoritative source is unavailablethe authoritative source is unavailable
audit.unavailable503Required audit is unavailablerequired audit is unavailable
service.not_ready503Service is not readythe service is not ready
internal.timeout504Request timed outrequest exceeded the configured timeout

The consultation.* codes cover record reads, lookups, and searches. The aggregate-data.* codes cover the SDMX data and structure routes. The pairs are deliberate: a consultation and an aggregate data request that fail the same way still carry different codes, so a caller can tell which surface refused without inspecting the path.

Four distinctions are worth knowing before writing client code.

  • resource.not_found means the path does not name anything this caller may reach. consultation.unresolved means the operation exists and ran, and no record answered it. A caller that is authenticated but not entitled to a resource gets the same resource.not_found as a caller asking for a resource that was never declared, so the code cannot be used to probe what exists. The exception is deliberate: an anonymous caller asking for something whose class has any protected operation gets auth.missing_credential first, so a legitimate client is told to authenticate rather than told the path is wrong.
  • auth.invalid_credential collapses every token validation failure into one answer: expiry, signature, issuer, audience, unknown key, and disallowed algorithm are indistinguishable to the caller.
  • filter.unknown_field fires when a filter is not declared for the operation being called, not when a column is absent from the source. The compiled contract decides what a caller may filter on, so an undeclared filter and a nonexistent column look the same.
  • The three 413 codes separate an oversized request from an oversized answer. internal.payload_too_large is about the request body, consultation.response_too_large is about the record answer, and aggregate-data.too_large is about the observation count an SDMX request would return.

Relay compiles and verifies its sealed package before it serves anything. A failure at that stage is not an HTTP problem: the process writes a bounded operational record on standard error and exits, or /ready answers service.not_ready while a dependency is not yet usable. There is no startup-only code namespace in the problem set, because a Relay that cannot activate its package does not begin serving. See Operate Relay for what those startup records look like.

Evidence Gateway has its own closed problem set, documented at Evidence Gateway problem types. That page is the single place the set is written down, because the set is frozen in products/evidence/contracts/problem-contract.yaml and CI byte-diffs the generated schema against it; a second copy of the table here would drift without a gate to catch it.

Evidence Gateway type URIs use https://id.registrystack.org/problems/registry-evidence/<code-path>, where the dotted code is written with slashes in the URI path. Every problem body has exactly type, title, status, detail, code, and traceId members. Its static, value-free detail and public trace identifier never disclose subject data, source diagnostics, or the server-minted audit identifier.

Registry Mint is an OAuth 2.0 token endpoint, so it does not use RFC 9457 at all. Every error response body is the OAuth form {"error": "<code>"} over four codes (invalid_request, unsupported_grant_type, invalid_client, and server_error), transcribed with their statuses and causes in the Registry Mint reference. The one to know before reading it: every client authentication failure collapses to invalid_client, so the endpoint cannot be used to probe which client ids are registered.

The Relay codes above are transcribed from ProblemCode in crates/registry-relay-v2/src/problem.rs, which owns the code strings, titles, details, statuses, type URIs, and response headers in one place. The trace behavior is TraceContext in the same file.

The codes this page points at rather than repeating are transcribed on their own pages from crates/registry-evidence/src/problem.rs with products/evidence/contracts/problem-contract.yaml (Evidence Gateway) and crates/registry-mint/src/error.rs (Registry Mint).