Skip to content
Registry StackDocsDevelopment (unreleased)

Error and status code reference

View as Markdown

Registry Stack HTTP surfaces use product-specific error contracts. This page carries Relay’s set in full and points to the Evidence Gateway and OID4VCI supporting-service contracts.

Relay returns errors as RFC 9457 problem details with the media type application/problem+json. The set is closed: ProblemCode in crates/registry-relay-http-contract/src/lib.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 a known consultation did not resolve exactly one record. It also covers an invalid record identifier rejected before a source query, so callers must not infer that source execution occurred. 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 and checks initial readiness before binding its listener. A failure at that stage is not an HTTP problem: the process writes a bounded operational record on standard error and exits. After serving begins, /ready can answer service.not_ready if readiness is later lost. There is no startup-only code namespace in the problem set. 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. The runtime-owned generator reproduces the committed schema and OpenAPI artifacts under CI; the frozen products/evidence/contracts/problem-contract.yaml is maintained separately.

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.

The OID4VCI delivery service has its own closed protocol error vocabulary in the committed v0.21.0 OpenAPI artifact. Framework-level refusals that never enter the protocol handler can intentionally use text/plain.

The Relay catalog is ProblemCode in crates/registry-relay-http-contract/src/lib.rs. Runtime serialization and response headers are in crates/registry-relay-v2/src/problem.rs; trace handling is TraceContext in crates/registry-platform-httpsec/src/server.rs.

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).