Error Codes

Every code on this page is emitted by the current implementation. The Go codes are produced by verify.Chain (drs-verify/pkg/verify/chain.go); the SDK codes are thrown by the issuance path (drs-sdk/src/sdk).

SDK errors (TypeScript)

Thrown by the SDK during issuance. They fire before any signing occurs — an invalid chain cannot be created.

CodeThrown byDescriptionFix
MISSING_CONSENTissueRootDelegationrootType is "human" but no consent was suppliedPass a consent record to issueRootDelegation
POLICY_ESCALATIONissueSubDelegationChild policy field exceeds the parent constraintReduce the escalating field to within parent bounds
MALFORMED_JWTissueSubDelegation, issueInvocationA parent receipt passed in could not be decoded as a 3-part JWTPass the exact JWT strings returned by the issuance functions
EMPTY_CHAINbuildBundle, createInvocationBundleNo delegation receipts were suppliedInclude at least the root delegation receipt
MISSING_INVOCATIONbuildBundleNo invocation receipt was suppliedInclude the signed invocation receipt
INVALID_OPERATOR_CONFIGvalidateOperatorConfig, parseOperatorConfigMissing required field or invalid valueCheck the field named in the error message

The SDK does not throw TEMPORAL_BOUNDS_VIOLATION at issuance. Temporal nesting is enforced by the verifier (see the Go table, Block D).

Verification errors (Go)

Returned by verify.Chain inside the error object of the /verify response. The response shape and HTTP status are described in Response shape below — note that POST /verify always returns HTTP 200, even on failure.

Block A — Completeness

CodeDescription
EMPTY_CHAINbundle.receipts is empty
CHAIN_TOO_DEEPMore than MAX_CHAIN_DEPTH (16) receipts; checked before any cryptographic work
MISSING_INVOCATIONbundle.invocation is empty
MALFORMED_RECEIPTA receipt JWT could not be decoded
MALFORMED_INVOCATIONThe invocation JWT could not be decoded

Block B — Structural Integrity

CodeDescription
WRONG_TYPEA receipt is not a delegation-receipt, or the invocation is not an invocation-receipt
VERSION_MISMATCHdrs_v is not 4.0
INVALID_JTIA receipt jti lacks the dr: prefix, or the invocation jti lacks the inv: prefix
CHAIN_STRUCTUREreceipts[0] (the root) carries a prev_dr_hash
MISSING_CONSENTRoot drs_root_type is human but drs_consent is absent
INVALID_CONSENTdrs_consent is present but a required field (method, timestamp, session_id, policy_hash) is empty
CHAIN_BREAKA non-root receipt's prev_dr_hash is missing or does not match SHA-256 of the previous receipt
ISSUER_MISMATCHreceipts[i].issreceipts[i-1].aud
INVOKER_MISMATCHinvocation.iss ≠ the leaf receipt's aud
CHAIN_REFERENCE_MISMATCHinvocation.dr_chain length or a hash entry does not match the receipts

Block C — Cryptographic Validity

CodeDescription
UNRESOLVABLE_DIDAn issuer DID could not be resolved to an Ed25519 public key
INVALID_SIGNATUREA receipt's Ed25519 signature failed verification
INVALID_INVOCATION_SIGNATUREThe invocation's Ed25519 signature failed verification
SIGNATURE_MALLEABILITYSignature rejected for S ≥ L (Ed25519 strict-mode violation)

Block D — Semantic / Policy Validity

CodeDescription
INVALID_ROOT_CMDThe root receipt cmd is empty or not an absolute path beginning with /
COMMAND_MISMATCHA receipt cmd widens its parent, or invocation.cmd is not equal to / a sub-path of the leaf cmd
SUBJECT_MISMATCHDelegation receipts do not all share the same sub
INVOCATION_SUBJECT_MISMATCHinvocation.sub ≠ the chain's root sub
TOOL_SERVER_MISMATCHinvocation.tool_serverSERVER_IDENTITY. Also returned fail-closed when tool_server is set but the verifier has no SERVER_IDENTITY configured
POLICY_ESCALATIONA sub-delegation's policy escalates beyond its parent
TEMPORAL_BOUNDS_VIOLATIONChild nbf < parent nbf, child exp > parent exp, or child omits exp while the parent sets one
POLICY_VIOLATIONinvocation.args exceed a policy constraint in the chain

Block E — Temporal Validity

CodeDescription
INVOCATION_NOT_YET_VALIDinvocation.iat is in the future beyond the allowed clock skew (5 min)
INVOCATION_STALEinvocation.iat is older than the maximum invocation age (the nonce store TTL). Bounds the replay window after a JTI is evicted
NOT_YET_VALIDnow < a receipt's nbf
EXPIREDnow > a receipt's exp (receipts with no exp never expire)

Block F — Revocation

CodeDescription
REVOCATION_CHECK_FAILEDThe Bitstring Status List could not be fetched (fail-closed)
REVOKEDA receipt is revoked, either in the remote status list or the local /admin/revoke store

Response shape

POST /verify always returns HTTP 200. Determine the outcome from the valid field, not the status code. (HTTP 403 is returned only by the MCP/A2A middleware routes, which gate a request — not by /verify itself.)

A failed verification:

{
  "valid": false,
  "error": {
    "code": "CHAIN_BREAK",
    "message": "receipt[1] prev_dr_hash mismatch: claimed \"sha256:def456...\", expected \"sha256:abc123...\".",
    "suggestion": "DR at index 0 may have been modified after DR at index 1 was issued, or the receipts are in the wrong order."
  }
}

error is an object with three string fields:

  • code — the stable machine-readable code from the tables above
  • message — a full English sentence describing the specific failure
  • suggestion — concrete remediation guidance

There is no top-level block field on the response; the block is a documentation grouping, not a wire field.