OpenFreyt Protocol — RFC 9457 Error Catalogue
Generated from
specification/errors/error-catalogue.md. Edit the canonical source file, not this page.
All error responses from the OpenFreyt Protocol API use RFC 9457 Problem Details
(application/problem+json). Every error document MUST include type, title,
and status. Implementors SHOULD include detail and correlationId to aid
debugging and support.
Base schema: specification/schemas/problem-detail.schema.json
Type URI Convention
Section titled “Type URI Convention”All protocol-specific error type URIs follow the form:
urn:freyt:protocol:error:{code}where {code} is the kebab-case identifier defined in this catalogue.
Generic HTTP errors (e.g. about:blank) are permitted for conditions not
listed here.
Error Types
Section titled “Error Types”1. validation-error — HTTP 400
Section titled “1. validation-error — HTTP 400”Type URI: urn:freyt:protocol:error:validation-error
Title: Validation Error
Status: 400
Description: One or more request body fields failed schema or business-rule
validation. The errors array extension field contains per-field details.
Extension Fields:
| Field | Type | Description |
|---|---|---|
errors |
array | Array of { field, message, code } objects (RFC 6901 pointer path). |
Detail Format: "Request body validation failed; see errors for per-field details."
Example:
{ "type": "urn:freyt:protocol:error:validation-error", "title": "Validation Error", "status": 400, "detail": "Request body validation failed; see errors for per-field details.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBA", "errors": [ { "field": "/payload/equipmentType", "message": "Required field missing.", "code": "required" }, { "field": "/payload/origin/locode", "message": "Must be a valid UN/LOCODE.", "code": "format" } ]}2. authentication-required — HTTP 401
Section titled “2. authentication-required — HTTP 401”Type URI: urn:freyt:protocol:error:authentication-required
Title: Authentication Required
Status: 401
Description: The request lacks valid authentication credentials. The caller
must obtain a valid bearer token before retrying.
Extension Fields: none
Detail Format: "A valid bearer token is required to access this resource."
Example:
{ "type": "urn:freyt:protocol:error:authentication-required", "title": "Authentication Required", "status": 401, "detail": "A valid bearer token is required to access this resource.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBB"}3. authorization-denied — HTTP 403
Section titled “3. authorization-denied — HTTP 403”Type URI: urn:freyt:protocol:error:authorization-denied
Title: Authorization Denied
Status: 403
Description: The caller is authenticated but lacks the required OAuth2 scope
or organization-level permission to perform the operation. See also the
404 vs 403 rule: if the resource does not exist and the caller is not a partner,
404 is returned instead of 403 to prevent existence leakage.
Extension Fields:
| Field | Type | Description |
|---|---|---|
requiredScope |
string | The OAuth2 scope required for this operation. |
Detail Format: "Insufficient scope; '{requiredScope}' is required."
Example:
{ "type": "urn:freyt:protocol:error:authorization-denied", "title": "Authorization Denied", "status": 403, "detail": "Insufficient scope; 'freyt:capacity-offer:write' is required.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBC", "requiredScope": "freyt:capacity-offer:write"}4. resource-not-found — HTTP 404
Section titled “4. resource-not-found — HTTP 404”Type URI: urn:freyt:protocol:error:resource-not-found
Title: Resource Not Found
Status: 404
Description: The referenced resource does not exist, or the caller is not in
an active partner relationship with the resource owner and the protocol’s
existence-leakage policy applies (PROTOCOL.md §R-001).
Extension Fields:
| Field | Type | Description |
|---|---|---|
resourceType |
string | The resource type that was not found. |
resourceId |
string | The ID that was requested, if safe to echo. |
Detail Format: "The requested {resourceType} was not found."
Example:
{ "type": "urn:freyt:protocol:error:resource-not-found", "title": "Resource Not Found", "status": 404, "detail": "The requested CapacityOffer was not found.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBD", "resourceType": "CapacityOffer", "resourceId": "co_01HYZ000000000000000"}Note (Existence Leakage): When a non-partner requests a resource that exists but belongs to an unrelated organization, the protocol MUST return 404 (not 403) to avoid leaking the existence of the resource. Implementors MUST NOT vary the response body between “truly not found” and “hidden” cases.
5. relationship-inactive — HTTP 409
Section titled “5. relationship-inactive — HTTP 409”Type URI: urn:freyt:protocol:error:relationship-inactive
Title: Relationship Inactive
Status: 409
Description: The operation requires an active PartnerRelationship between
the two organizations, but no active relationship exists. The relationship may
be in draft, requested, suspended, or terminated state.
Extension Fields:
| Field | Type | Description |
|---|---|---|
relationshipId |
string | ID of the relevant PartnerRelationship, if one exists. |
relationshipState |
string | Current state of the relationship. |
requiredState |
string | State required for the operation (always active). |
Detail Format: "An active PartnerRelationship is required; current state is '{relationshipState}'."
Example:
{ "type": "urn:freyt:protocol:error:relationship-inactive", "title": "Relationship Inactive", "status": 409, "detail": "An active PartnerRelationship is required; current state is 'suspended'.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBE", "relationshipId": "pr_01HYZ000000000000001", "relationshipState": "suspended", "requiredState": "active"}6. stale-resource-version — HTTP 409
Section titled “6. stale-resource-version — HTTP 409”Type URI: urn:freyt:protocol:error:stale-resource-version
Title: Stale Resource Version
Status: 409
Description: The If-Match header value does not match the resource’s current
resourceVersion. The caller must fetch the latest version and retry.
Extension Fields:
| Field | Type | Description |
|---|---|---|
currentVersion |
string | The resource’s current version (ETag value). |
submittedVersion |
string | The version submitted by the caller. |
Detail Format: "The submitted resourceVersion is stale; fetch the current resource and retry."
Example:
{ "type": "urn:freyt:protocol:error:stale-resource-version", "title": "Stale Resource Version", "status": 409, "detail": "The submitted resourceVersion is stale; fetch the current resource and retry.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBF", "currentVersion": "3", "submittedVersion": "2"}7. idempotency-conflict — HTTP 409
Section titled “7. idempotency-conflict — HTTP 409”Type URI: urn:freyt:protocol:error:idempotency-conflict
Title: Idempotency Conflict
Status: 409
Description: A request with the same Idempotency-Key was previously received
with a different request payload (fingerprint mismatch). The protocol’s
idempotency guarantee cannot be satisfied. The caller must use a new
Idempotency-Key or resend the identical payload.
Extension Fields:
| Field | Type | Description |
|---|---|---|
idempotencyKey |
string | The conflicting idempotency key. |
Detail Format: "A prior request with the same Idempotency-Key had a different payload; use a new key or resend the identical payload."
Example:
{ "type": "urn:freyt:protocol:error:idempotency-conflict", "title": "Idempotency Conflict", "status": 409, "detail": "A prior request with the same Idempotency-Key had a different payload; use a new key or resend the identical payload.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBG", "idempotencyKey": "idem_01HYZABCDEF000000001"}8. invalid-state-transition — HTTP 422
Section titled “8. invalid-state-transition — HTTP 422”Type URI: urn:freyt:protocol:error:invalid-state-transition
Title: Invalid State Transition
Status: 422
Description: The requested command is not a legal transition from the
resource’s current state. Consult the state-transition tables in
specification/state-machines/ for legal transitions.
Extension Fields:
| Field | Type | Description |
|---|---|---|
resourceType |
string | The resource type (e.g. CapacityOffer). |
resourceId |
string | The resource ID. |
currentState |
string | The resource’s current state. |
requestedCommand |
string | The command that was attempted. |
legalCommands |
array | Commands that are legal from the current state. |
Detail Format: "Command '{requestedCommand}' is not valid for a {resourceType} in state '{currentState}'."
Example:
{ "type": "urn:freyt:protocol:error:invalid-state-transition", "title": "Invalid State Transition", "status": 422, "detail": "Command 'publish' is not valid for a CapacityOffer in state 'withdrawn'.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBH", "resourceType": "CapacityOffer", "resourceId": "co_01HYZ000000000000010", "currentState": "withdrawn", "requestedCommand": "publish", "legalCommands": []}9. resource-expired — HTTP 422
Section titled “9. resource-expired — HTTP 422”Type URI: urn:freyt:protocol:error:resource-expired
Title: Resource Expired
Status: 422
Description: The resource reached its validUntil deadline and was
automatically transitioned to an expired terminal state. No further commands
are accepted.
Extension Fields:
| Field | Type | Description |
|---|---|---|
resourceType |
string | The resource type (e.g. LoadRequest). |
resourceId |
string | The resource ID. |
expiredAt |
string (dt) | ISO 8601 UTC timestamp when the resource expired. |
Detail Format: "The {resourceType} expired at {expiredAt} and no further commands are accepted."
Example:
{ "type": "urn:freyt:protocol:error:resource-expired", "title": "Resource Expired", "status": 422, "detail": "The LoadRequest expired at 2024-09-01T12:00:00Z and no further commands are accepted.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBI", "resourceType": "LoadRequest", "resourceId": "lr_01HYZ000000000000020", "expiredAt": "2024-09-01T12:00:00Z"}10. incompatible-decision-policy — HTTP 422
Section titled “10. incompatible-decision-policy — HTTP 422”Type URI: urn:freyt:protocol:error:incompatible-decision-policy
Title: Incompatible Decision Policy
Status: 422
Description: A MatchDecision was submitted but the decision type or terms
are incompatible with the governing MatchProposal’s policy constraints. For
example, submitting a COUNTER decision on a proposal that requires ACCEPT_OR_REJECT
only, or a counter with terms outside the permitted range.
Extension Fields:
| Field | Type | Description |
|---|---|---|
decisionType |
string | The decision type that was submitted. |
allowedTypes |
array | Decision types permitted for this proposal. |
policyRef |
string | Identifier of the governing policy constraint. |
Detail Format: "Decision type '{decisionType}' is not permitted by the MatchProposal policy."
Example:
{ "type": "urn:freyt:protocol:error:incompatible-decision-policy", "title": "Incompatible Decision Policy", "status": 422, "detail": "Decision type 'COUNTER' is not permitted by the MatchProposal policy.", "correlationId": "req_01HYZ4K2C3JVNSQ4WXPBJ", "decisionType": "COUNTER", "allowedTypes": ["ACCEPT", "REJECT"], "policyRef": "policy_accept_or_reject_only"}Summary Table
Section titled “Summary Table”| Code | HTTP | Description |
|---|---|---|
validation-error |
400 | Request body field validation failure |
authentication-required |
401 | Missing or invalid bearer token |
authorization-denied |
403 | Valid token lacks required scope or organization permission |
resource-not-found |
404 | Resource absent or hidden by existence-leakage policy |
relationship-inactive |
409 | No active PartnerRelationship between the organizations |
stale-resource-version |
409 | If-Match version mismatch; fetch latest and retry |
idempotency-conflict |
409 | Same Idempotency-Key reused with a different payload |
invalid-state-transition |
422 | Command not legal from the resource’s current state |
resource-expired |
422 | Resource reached its validUntil deadline; terminal |
incompatible-decision-policy |
422 | MatchDecision type violates proposal policy constraints |
Integration Points
Section titled “Integration Points”- The base schema
specification/schemas/problem-detail.schema.jsondefinestype,title,status,detail,instance,correlationId, and theerrorsarray. - All
4xx/5xxresponses inspecification/openapi/openapi.yamlreference../schemas/problem-detail.schema.jsonvia$ref. - Extension fields documented above are additional properties not restricted by
the base schema (
additionalPropertiesis unset, so they pass validation). - Implementors MUST set
Content-Type: application/problem+jsonon all error responses. correlationIdSHOULD be present on every error response to enable distributed tracing and support ticket lookup.