Skip to content

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


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.


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" }
]
}

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"
}

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"
}

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.


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"
}

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"
}

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"
}

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": []
}

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"
}

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

  • The base schema specification/schemas/problem-detail.schema.json defines type, title, status, detail, instance, correlationId, and the errors array.
  • All 4xx/5xx responses in specification/openapi/openapi.yaml reference ../schemas/problem-detail.schema.json via $ref.
  • Extension fields documented above are additional properties not restricted by the base schema (additionalProperties is unset, so they pass validation).
  • Implementors MUST set Content-Type: application/problem+json on all error responses.
  • correlationId SHOULD be present on every error response to enable distributed tracing and support ticket lookup.