ZIFFER home

Every refusal, and what it means

The names the receipt verifier raises, the names the decision API sends, and the classes the executor alerts on - with what to do about each.

Every refusal carries a name. The name is the machine-readable half and the message beside it is for a person; dispatch on the name, log the message.

A refusal is not a network error, and you should not retry it. A receipt that fails one of these fails it the same way every time, so asking again gets the same answer with an extra round trip. Treat it as you would a failed authorisation: stop, and record the name.

What the receipt verifier refuses

Every refusal names what is wrong with the receipt, and the rule of the specification it was refused under. Both SDKs print the same line for the same refusal, Name: what it means (rule): read name and clause on the refusal, and action for what to do. Each rule links to its section of the specification.

RefusalWhat it meansWhat you doRule
ReceiptVersionUnsupportedWhat you passed as the receipt is not a version 3 receipt.Pass the receipt exactly as the decision API returned it, unchanged. If you did, report it with the receipt.AB-0
ReceiptTooLargeThe receipt's signed body is larger than the 4,096 bytes a receipt is signed within.Do not act on the receipt and do not retry. Report it with the receipt.AB-6
SuiteUnknownThe receipt names a signature suite this verifier does not know or names none, or the suite floor you configured is not a suite it knows.Check the spelling of the suite floor you configured (ZIFFER_SUITE_FLOOR), then update the SDK. If both are right, report it with the receipt.CR-1
SuiteBelowFloorThe receipt, or an approval sent with it, is not signed with a suite that contains every algorithm your suite floor requires.Confirm the suite floor you configured is the one you meant. If it is, do not act on the receipt and report it with the receipt.CR-4
NotCanonicallyEncodableThe receipt, an approval or the proposal you passed holds a value with no canonical encoding, such as a fractional number, or the proposal bytes are not UTF-8 JSON.Pass the exact proposal bytes you sent and the receipt exactly as it was returned. Remove any step that re-encodes either one on the way.AT-8a
SignatureInvalidThe receipt's signature does not verify under your trust anchor's keys.Do not act on the receipt and do not retry. Confirm your trust anchor is the one on your handover sheet, then report it with the receipt.9.3-1
DecisionNotAllowThe receipt records a decision other than ALLOW.Do not run the action. Report it with the receipt.9.3-2
ReceiptNotBoundToProposalThe receipt is not bound to the proposal you passed.Pass the exact proposal this receipt answered. When several calls are in flight, match each receipt to its own proposal before verifying.9.3-3
ReceiptTimeUnreadableThe receipt's issue or expiry time is missing or is not an RFC 3339 UTC instant.Do not act on the receipt. Pass it exactly as the decision API returned it; if you did, report it with the receipt.9.3-5
ReceiptExpiredYour clock is past the receipt's expiry time.Keep the verifying host's clock on UTC time, and verify a receipt as soon as it arrives: it is valid for 120 seconds. Propose the action again for a new receipt.9.3-5
ReceiptIssuedInFutureThe receipt's issue time is more than 5 seconds ahead of your clock.Synchronise the verifying host's clock with UTC time, then verify the receipt again.9.3-5
ValidityWindowTooLongThe receipt is valid for longer than the 120 seconds a receipt may be.Do not act on the receipt and do not retry. Report it with the receipt.L-14
NonceMalformedA nonce in the receipt, or in an approval sent with it, is not b64: followed by padded base64.Do not act on the receipt and do not retry. Report it with the receipt.WE-4
ReceiptNonceWrongSizeThe receipt's nonce is well formed but is not 128 bits long.Do not act on the receipt and do not retry. Report it with the receipt.L-17
ApprovalsUnverifiableThe approvals sent with the receipt cannot be matched to the list the receipt signed: a list is malformed, an approval is not on it, or the verifier was given no attester registry.Give the verifier your attester registry (ZIFFER_ATTESTER_REGISTRY, or the registry argument). If you did, do not act on the receipt and report it with the receipt.AB-1
ApprovalDigestsUnsortedThe receipt's list of approval digests is not in sorted order.Do not act on the receipt and do not retry. Report it with the receipt.AB-2
ApprovalDigestsDuplicatedThe receipt's list of approval digests names one approval more than once.Do not act on the receipt and do not retry. Report it with the receipt.AB-3
ApprovalCountMismatchThe number of approvals sent with the receipt is not the number of approvals the receipt signed.Pass the receipt and its approvals exactly as the decision API returned them. If you did, report it with the receipt.AB-4
ApprovalsMissingThe receipt commits to approvals and none were sent with it.Pass the receipt with its attestations exactly as the decision API returned them. If you did, report it with the receipt.INV-1-HIGH
ApprovalObjectMissingAn approval sent with the receipt carries no approval object.Do not act on the receipt and do not retry. Report it with the receipt.AT-8
ApprovalObjectMalformedAn approval object lacks a required field, carries a field that is not defined, or holds a field of the wrong type.Do not act on the receipt and do not retry. Report it with the receipt.AT-8b
ApprovalNonceWrongSizeAn approval's nonce is well formed but is not 128 bits long.Do not act on the receipt and do not retry. Report it with the receipt.AT-1
ApprovalSignatureInvalidAn approval's signature does not verify against the key your attester registry holds for that approver, or your registry does not enrol that approver.Check that the approver is enrolled in your attester registry with their current key. If they are, do not act on the receipt and report it with the receipt.9.3-7b-i
ApprovalNotBoundToProposalAn approval sent with the receipt is not bound to the proposal you passed.Do not act on the receipt and do not retry. Report it with the receipt and the proposal.9.3-7b-ii
ApprovalContextMismatchAn approval names another policy version than the receipt, expired before the receipt was issued, or has an unreadable policy, expiry or operator field.Propose the action again so that it is approved under the current policy. If it happens again, report it with the receipt.9.3-7b-iii
ApprovalsDisagreeOnOperatorThe approvals sent with the receipt name different operators.Do not act on the receipt and do not retry. Report it with the receipt.9.3-7b-iii-a
ApprovalQuorumSizeMismatchAn approver consented to a number of required approvals other than the quorum your attester registry sets.Propose the action again so that it is approved under the current policy. If it happens again, report it with the receipt.AT-9
ApprovalIdMismatchAn approval's transmitted identifier is not the identifier computed from its content.Do not act on the receipt and do not retry. Report it with the receipt.Y1b
ApproverAssuranceTooLowAn approver is enrolled at an assurance level below your attester registry's floor, or at a level outside AS0 to AS2.Enrol the approver at the assurance level your registry requires, or have an approver enrolled at that level approve instead.AT-10
QuorumNotMetFewer distinct approvers approved the action than the quorum your attester registry sets.Do not run the action. Check your registry's approvers and roles against who approved; propose the action again once enough approvers can answer.AT-3
OperatorApprovedOwnActionThe operator who proposed the action is counted among its approvers.Do not run the action. Have approvers other than the operator approve it.AT-2
ApproverKeyTypeMismatchAn approval claims a kind of key, a person's passkey or a machine key, that is not the kind your attester registry enrols for that approver.Do not act on the receipt and do not retry. Report it with the receipt.WebauthnAlgMismatch
PasskeyCounterNotAdvancedA passkey or security key's signature counter did not advance, so the credential was copied or the approval was replayed.Do not act on the receipt. Treat the approver's authenticator as compromised and re-enrol it before it approves anything else.WebauthnCounter
ApprovalMalformedA person's approval, or the key your attester registry holds for them, is not in the shape a passkey approval takes.Check the approver's entry in your attester registry against the credential they enrolled. If it matches, report it with the receipt.Malformed
PasskeyAssertionTypeInvalidA person's approval is not a passkey sign-in assertion.Ask the approver to approve again from your approval page.WebauthnType
PasskeyChallengeMismatchA person's approval answers a different request than this approval.Do not act on the receipt. Ask the approver to approve again from your approval page; if it happens again, report it with the receipt.WebauthnChallenge
PasskeyOriginMismatchA person's approval was made on a site other than your approval page.Ask the approver to approve again from your own approval page. If they did, report it with the receipt.WebauthnOrigin
PasskeyAuthenticatorDataInvalidA person's approval names another site, or does not show that the person was present and verified.Ask the approver to approve again and to complete the fingerprint, face or PIN step their authenticator asks for.WebauthnAuthenticatorData
PasskeySignatureInvalidA person's approval signature does not verify against the passkey or security key your attester registry holds for them.Check that the approver's entry in your attester registry holds their current credential. If it does, do not act on the receipt and report it with the receipt.WebauthnSignature

verify runs every check answerable from a receipt, a proposal and a key. Single use is checked against the ledger inside our deployment, not by verify: act on each receipt once.

What the decision API refuses

These names arrive verbatim from the API. Both SDKs surface them without guessing.

NameHTTPWhat it meansWhat you do
ProposalMalformed400The body is not a valid proposal.Fix the body. No clause travels with this one.
ApiKeyUnknown401The key is not one we hold.An expired key answers exactly like a key that never existed, byte for byte, so check the expiry date you were given first.
TenantMismatch403The body names a tenant that is not the key's.Send the key's tenant. The gateway refuses rather than rewriting your body, because a gateway that edited it would have become its author.
ListQueryMalformed400The query string of GET /v1/decisions is not a legal one.Drop every parameter, confirm the call works, then add them back one at a time.
DecisionUnknown404No decision with that id.Check the locator you are reading with.
RateLimited429This key's allowance on the decision routes is spent. Nothing was decided.Wait the seconds Retry-After names, then send the same request again.
AdmissionUnavailable502We could not reach the component that decides.Retry this one. Nothing was decided, and the row stays pending.
GatewayUnavailable503The process serving the API could not answer at all.A short retry is reasonable. Nothing decided anything.
QueueFull503The approval queue is full, so the submission was not taken. No row was written.Wait the seconds Retry-After names and send the same bytes again.

The set is open on purpose: the API may name a refusal that is not in this table, and both clients carry any name through verbatim rather than folding it into one of these.

A decision the API did answer, and refused, carries no clause id. It carries a refusal_category, which says whether to try again and nothing about which rule refused. The rule is in the console and in your audit chain.

CategoryWhat it meansWhat you do
PolicyRefusedA rule refused the action.Never retry the same proposal.
PolicyBasisMovedThe policy changed under the decision, in the window right after a publish.Retry once, after the activation window.
RateBoundedA rate bound refused it.Retry later.
ProposalMalformedThe submission is not a valid proposal.Fix the submission.

What your SDK raises on its own

NameLanguageWhat it means
RefusedErrorPythonThe receipt did not hold. name is the refusal, clause the rule it was refused under, and action what to do.
RefusalTypeScriptThe same, thrown, with the same name, clause and action.
ApiErrorPythonThe API refused, or answered outside its contract. name is the API's name, or UnexpectedResponse.
ApiRefusalTypeScriptThe API refused. error is the API's name.
GatewayUnreachablePythonAn ApiError name, status 0: no HTTP answer at all, such as a refused connection, DNS or a timeout. Named apart from the API's own outage because the advice differs.
UnexpectedResponsePythonAn ApiError name: an answer that was not the contract's shape. It fails closed under its own name rather than being guessed into one of the API's.
ResponseMalformedTypeScriptThe same, thrown. An SDK that repairs a malformed answer is an SDK that invents decision state.
WaitTimeoutbothThe deadline passed with the decision still pending. Not a refusal and not an answer: the decision may still decide, and the id remains readable.
DeadlineExceededbothThe deadline you passed would be crossed by the next retry, so the client stopped before sleeping. It carries the last failure's name and status.
AnchorErrorPythonYour trust anchor document could not be accepted. Omitting the suite floor raises this one, naming MinSuiteRequired. It is a problem on your side, kept distinct from a verdict about any receipt.

A ValueError from Python's verify is not a refusal. If the bytes are not JSON at all, it raises that deliberately, so that a broken integration is not mistaken for a caught attack.

What the executor refuses

When an executor refuses a receipt, the refusal reaches two places: an audit record, and a critical alert whose audience is named in your signed bundle rather than by us. Each alert carries one of eight classes.

ClassWhat raised it
LEDGER_REPLAYA consumed identifier, or a rebound origin.
RECOMPUTATION_MISMATCHThe receipt's stated risk or reversibility is not what your bundle says.
SUITE_BELOW_FLOORA signature suite under your floor.
CONTEXT_STOREThe live capability check disagrees with what was decided.
AUDIT_INTEGRITYThe audit chain, or its anchors.
ATTESTATION_INVALIDA quorum that does not hold, including a human approver's assertion.
DEFERRED_RELEASEThe hold, the notice, or an acknowledgement.
RECEIPT_INVALIDAnything else the executor refuses on the receipt's own bytes.

Two behaviours to know before you build alerting on these.

A refusal is terminal, not a retry. The work closes as refused. Nothing is lost by that: the refusal is already an audit record and already a critical alert, and leaving it open would wedge every later item behind one poisoned receipt.

A dependency that could not answer is not a refusal. If the audit service, the context store or the ledger cannot be reached, the executor reaches no verdict, reports nothing, and the work stays queued. A store that could not answer is not a store that said no, and recording a verdict nobody reached would close a receipt nobody decided.

On this page