Skip to main content

Specified error model

The platform’s normative error specification (docs/api/03-error-model.md) defines every error response as:
{
  "error": {
    "code": "POLICY_NOT_FOUND",
    "message": "Requested policy version does not exist."
  }
}
with code as a stable, machine-readable identifier and message as a human-readable description, and a fixed set of canonical codes grouped by category:
CategoryCodes
ValidationINVALID_REQUEST, INVALID_METADATA, INVALID_POLICY, INVALID_SIGNALS, INVALID_SCHEMA
AuthenticationUNAUTHENTICATED, INVALID_TOKEN, TOKEN_EXPIRED
AuthorizationFORBIDDEN, INSUFFICIENT_PERMISSIONS
ResourceBUSINESS_TRANSACTION_NOT_FOUND, EXECUTION_NOT_FOUND, RECEIPT_NOT_FOUND, POLICY_NOT_FOUND, VERIFICATION_NOT_FOUND
ConflictDUPLICATE_TRANSACTION, OVERRIDE_NOT_ALLOWED, IMMUTABLE_RESOURCE
VerificationVERIFICATION_FAILED, INVALID_SIGNATURE, TRUST_RECORD_CORRUPTED, POLICY_MISMATCH
InternalINTERNAL_ERROR, STORAGE_FAILURE, UNEXPECTED_ERROR
HTTP StatusCategoryExample
400ValidationINVALID_REQUEST
401AuthenticationUNAUTHENTICATED
403AuthorizationFORBIDDEN
404ResourcePOLICY_NOT_FOUND
409ConflictDUPLICATE_TRANSACTION
422Verification / ValidationVERIFICATION_FAILED
500InternalINTERNAL_ERROR

What the implementation actually returns today

Discrepancy. packages/api/src/middleware/error-handler.ts does not use the nested { "error": { "code", "message" } } structure above. It returns a flat shape:
{ "error": "Requested policy version does not exist." }
and only attaches a top-level code field for errors that are instances of RuntimeError:
{ "error": "...", "code": "SOME_RUNTIME_CODE" }
Individual route handlers (execute.ts, receipt.ts, replay.ts, verify.ts) also return their own ad hoc { "error": "..." } bodies directly for request-shape validation (e.g. missing/invalid businessTransactionId), before the transaction even reaches the error handler.

Practical status mapping (as implemented)

RouteFailure conditionStatusBody shape
POST /executeInvalid or missing businessTransactionId400{ "error": string }
POST /policies/validateMissing policyId / policyVersion400{ "valid": false, "errors": string[] }
POST /policies/validatePolicy not found404{ "valid": false, "errors": string[] }
GET /transactions/:idNot found404{ "error": string }
GET /trust-records/:idNot found404{ "error": string }
GET /verify/:id, GET /receipt/:idTrust record or sub-resource not found404{ "error": string }
Anything elseRuntimeError subclasserror.status{ "error": string, "code": string }
Anything elseUnhandled exception500{ "error": "Internal Server Error" }

Recommendation for integrators

Match on the error string’s presence and HTTP status code rather than a code field, until the error handler is updated to conform to the specified nested model. If you need stable machine-readable codes today, they’re only reliably present for errors that subclass RuntimeError.