A receipt is verified from its .kbundle file alone: in the dashboard (in your browser), with @kblockai/sdk/verify, or with the Rust kblock CLI. All three run the same seven checks and produce the same report. This page helps when the result is not the one you expect: an INVALID or NOT THIS GATEWAY verdict, a report with errors, or a bundle the Cloud API cannot serve.
Start with Read the report: the first failing check names the cause. Then go to the matching section below. To learn how to run a verification in the first place, see Verify a receipt.
Find your symptom
| What you see | Go to |
|---|---|
| NOT THIS GATEWAY, or an issuer that is not your gateway's KID | The receipt is not from your gateway |
| CONSISTENT in the dashboard | No gateway KID is pinned |
Bundle hash mismatch, ID_MISMATCH, INVALID_SIGNATURE | The bundle was changed |
UNSUPPORTED_VERSION, MALFORMED_JSON or another Bundle format error | The file is not a bundle this verifier reads |
RECEIPT_MISMATCH "covered objects differ", UNKNOWN_IDENTITY | An object is missing from the bundle |
"Could not load the receipt" or "Could not fetch the bundle" with "K-Block could not complete this request because of a problem on its side." or INVALID_STATE | The Cloud API cannot serve the bundle |
| MISMATCH on a receipt page | MISMATCH on a receipt page |
| Verification by the Cloud and the browser check disagree | The two dashboard verdicts disagree |
A warning or a note on a receipt that is VALID | Warnings and notes on a valid receipt |
The CLI ends with error: … and exit code 2 | Command-line errors |
Read the report
Every verifier reports seven checks, in this order: Bundle format, Identity chains, Signature chain, Receipt coverage, Graph integrity, Workflow & authorization, Receipt summary. Each one is VALID, INVALID or SKIPPED. A receipt is valid only when all seven are VALID and there are no errors.
Read it with three rules:
- The first
INVALIDcheck is the cause. Later checks build on earlier ones, so a failure often stops verification.SKIPPEDmeans "not reached", not "passed" and not a second failure. ASKIPPEDcheck can therefore appear above the failing one: anID_MISMATCHfound while the file is read leaves Identity chainsSKIPPEDabove the failing Signature chain, and Signature chain is completed only after Receipt coverage, so a coverage failure leaves itSKIPPEDtoo. - Each error has a code, usually the object it concerns, and a message. The code is stable and is what the sections below use. The message names the details, such as the object IDs involved.
- The text report shortens long values. The CLI's text output and
renderTextcut check details, messages and other text taken from the bundle with…so that they end before column 80, and say so in a note. For the full values, use the CLI's--jsonorreport.toJSON()in the SDK.
In the dashboard, the same report is the Verification in this browser panel: the Checks list ("N of 7 passed"), the Errors and Warnings notices, and What this verification cannot establish for the notes.
| Check | Fails when | Typical codes |
|---|---|---|
| Bundle format | The file is not JSON, not a K-Block bundle, a version this verifier does not support, too large, or an object in it is malformed. | MALFORMED_JSON, INVALID_BUNDLE, INVALID_ENVELOPE, UNSUPPORTED_VERSION, TOO_LARGE |
| Identity chains | The identity documents in the bundle do not form valid identity chains. | INVALID_IDENTITY, IDENTITY_EQUIVOCATION |
| Signature chain | An object no longer matches its ID (its content changed), a signature is wrong, or a signer's key is unknown or revoked. | ID_MISMATCH, INVALID_SIGNATURE, UNKNOWN_KEY, KEY_REVOKED, UNKNOWN_IDENTITY |
| Receipt coverage | The bundle's objects or identity documents are not exactly the ones the receipt commits to. | RECEIPT_MISMATCH, UNKNOWN_IDENTITY |
| Graph integrity | References between the signed steps are missing or broken, or the path does not lead to the receipt's root. | MISSING_REFERENCE, INCOMPLETE_GRAPH, RECEIPT_MISMATCH |
| Workflow & authorization | A step is not allowed by the workflow, or no valid delegation covers it. | INVALID_TRANSITION, UNAUTHORIZED_ACTOR, DELEGATION_REVOKED, CONSTRAINT_VIOLATION |
| Receipt summary | The summary or outcome the issuer signed differs from the one the verifier derives from the signed steps. | RECEIPT_MISMATCH |
The verifier never throws for a bad bundle: every problem is in the report. This program prints the cause, the findings and the issuer of a bundle. It compares the issuer with the Gateway KID in KBLOCK_GATEWAY_KID, an environment variable of your own that holds the Gateway KID K-Block gives you:
import { readFile } from "node:fs/promises";
import { verifyBundleJson } from "@kblockai/sdk/verify";
const file = process.argv[2] ?? "receipt.kbundle";
// The Gateway KID, the identity K-Block's Gateway signs with: the dashboard shows it after
// you create a gateway API key; if it does not, contact K-Block support.
const gatewayKid = (process.env.KBLOCK_GATEWAY_KID ?? "").trim();
const report = verifyBundleJson(await readFile(file, "utf8"));
// The first INVALID check is the cause; SKIPPED checks were not reached.
const cause = report.checks.find((check) => check.status === "INVALID");
console.log(cause === undefined ? "All checks passed." : `First failing check: ${cause.name}`);
for (const e of report.errors) console.log(`error ${e.code} ${e.object ?? ""}: ${e.message}`);
for (const w of report.warnings) console.log(`warning ${w.code}: ${w.message}`);
for (const n of report.notes) console.log(`note: ${n}`);
// A valid report does not say who issued the receipt: compare the issuer yourself.
console.log(`issuer: ${report.issuer ?? "(not read)"}`);
const fromYourGateway = report.valid && gatewayKid !== "" && report.issuer === gatewayKid;
if (report.valid && !fromYourGateway) console.log("The bundle verifies, but not as a receipt of your gateway.");
process.exit(fromYourGateway ? 0 : 1);See Verify a receipt for every part of the report, and Receipt fields for the fields of the receipt itself.
The receipt is not from your gateway
A valid report proves that a bundle is internally consistent and correctly signed by the identities it contains. Anyone can build such a bundle with their own keys. Whether the K-Block Gateway issued it is a separate check: the receipt's issuer must be your Gateway KID. Every K-Block Cloud receipt is issued under the gateway identity, also the receipts the Cloud API issues for rejections, cancellations and recovered executions.
Symptoms.
- The dashboard shows NOT THIS GATEWAY and the notice Not issued by this deployment's gateway, for example "The receipt was issued by
kid:kb:…, not by this deployment's gatewaykid:kb:……". The Issuer row is marked "not this deployment's gateway". - The notice can also name a step instead: the dashboard requires the pinned gateway to have signed the receipt's task, proposal, execution and result too.
- In your own code or with the CLI, the report is valid but
report.issuer(theIssued by:line) is not the KID you expect.
Causes and fixes.
| Cause | How to tell | Fix |
|---|---|---|
| The dashboard pins the wrong Gateway KID | Every receipt, including new ones, shows NOT THIS GATEWAY with the same issuer. Signing a delegation fails with a message that begins "The Cloud API reports the gateway identity …, but this dashboard trusts …" and says "Nothing was signed." Approval requests show the same message under "The signed evidence could not be verified — do not decide this request", and cannot be decided. | Do not rely on the browser verdict until this is fixed. Contact K-Block support and give them the issuer KID the notice shows. Meanwhile, verify with the SDK or the CLI against the Gateway KID that K-Block gave you. |
| K-Block replaced the Gateway identity | Receipts issued before the change show NOT THIS GATEWAY; receipts issued after it are VALID. | Expected: the dashboard compares every receipt with the one pinned KID. Verify older receipts with the SDK or the CLI against the KID that was in use when they were issued. |
| The bundle was not issued by K-Block Cloud | Usually a file checked under Verify a receipt file. The Organization (signed) row can show "another organization". | Do not rely on it as a K-Block Cloud receipt. If you know who issued it, verify it against that issuer's KID. |
| Your own check compares with the wrong value | The SDK or CLI report is valid, but your comparison fails. | Compare with the exact KID, kid:kb: prefix included and without surrounding whitespace or a trailing newline. |
Finding the right KID. The dashboard shows the Gateway KID right after you create a gateway API key on the API keys page ("The Gateway signs receipts as this identity"); if it does not show it, contact K-Block support. The KID on the API keys page is the one the dashboard pins, so it is wrong too when the pin is wrong: when it disagrees with the Gateway KID that K-Block gave you, trust the one K-Block gave you and contact K-Block support. GET /api/v1/gateway/identity on the Cloud API returns the currently registered kid and its identity documents, but that answer comes from the same service whose receipts you are checking, so prefer a value you recorded yourself. Nothing warns about a wrong pin in advance: it shows only in the browser, on receipt pages, in the form that signs a delegation and on approval requests. See Identities and KIDs.
A caution such as "The task was requested on behalf of …, which is not this organization's current principal (…); it may be a previous principal" is not a failure. It appears on receipts issued before the organization's principal was replaced, and the verdict stays VALID.
No gateway KID is pinned
CONSISTENT means every check passed against the identity documents inside the bundle, but the dashboard has not been told which Gateway identity to trust, so it cannot tell whether your gateway issued the receipt. The Issuer row is marked "gateway not pinned".
Compare the issuer KID with the Gateway KID that K-Block gave you, or verify the bundle with the SDK and compare its issuer with that KID, as in the program above. The dashboard normally has a pinned Gateway identity, so if it keeps showing CONSISTENT, contact K-Block support. Once the dashboard has a pinned Gateway identity, it shows VALID or NOT THIS GATEWAY instead.
The bundle was changed
Every K-Object's ID is a hash of its content, and every object is signed. Changing any signed byte is therefore detected. The verifier's test vectors show what each kind of change produces:
| Change | Code | Check that fails |
|---|---|---|
| The content of a signed step, for example the amount in the RESULT, a reference, or the receipt's own summary | ID_MISMATCH "object ID … does not match its content (recomputed …)" | Signature chain. Identity chains shows SKIPPED because the error stops verification while the file is read. |
| A signature | INVALID_SIGNATURE, then MISSING_REFERENCE for the step that refers to the broken one and RECEIPT_MISMATCH for the summary | Signature chain, Graph integrity, Receipt summary |
| A receipt re-signed by the issuer with a false summary | RECEIPT_MISMATCH "issuer's summary differs from the verified graph in: …", followed by the differing fields | Receipt summary |
On a receipt's page, the dashboard also compares the SHA-256 of the bundle it downloaded with the hash recorded at issuance. A difference makes the verdict INVALID with the notice Bundle hash mismatch: "The bundle's SHA-256 differs from the hash recorded when the receipt was issued."
Two changes are not tampering, but still change the file:
- Reformatting. Pretty-printing the JSON or changing its line endings keeps the bundle verifiable, because objects are hashed in their canonical form. The file's SHA-256 changes, though, so it no longer matches the SHA-256 on the receipt page or the
x-kblock-bundle-sha256header of the download. - A byte order mark. If a tool saved the file with a UTF-8 byte order mark at the start,
verifyBundleJsonrefuses it withMALFORMED_JSON"expected value at position 0" (the program above reads the file with Node.js, which keeps the mark). Some Windows tools add one when they save a text file. Remove the mark, or download the bundle again.
Fix. Download the bundle again with Download .kbundle on the receipt page, or from GET /api/v1/orgs/<org-id>/receipts/<receipt-id>/bundle, save the bytes unchanged, and verify the fresh copy. Compare its SHA-256 with the value on the receipt page. If the fresh copy verifies, the other copy was changed after it was downloaded. If it does not, contact K-Block support with the receipt ID and the report.
The file is not a bundle this verifier reads
A Bundle format failure stops verification: the checks after it show SKIPPED.
| Code | Example message | Cause |
|---|---|---|
UNSUPPORTED_VERSION | "unsupported bundle version '9.9'" | The file's kblock_bundle member is not "0.1". |
UNSUPPORTED_VERSION | "unsupported protocol version '0.2'" | An object in the bundle has a protocol version other than "0.1". |
MALFORMED_JSON | "expected value at position 0", "EOF while parsing a value" | The file is not JSON, is empty or cut short, or starts with a byte order mark. |
DUPLICATE_KEY, INVALID_NUMBER | — | JSON that K-Block's strict parser refuses: a member name that appears twice, or a number it does not allow, such as a fraction. |
INVALID_BUNDLE | "malformed bundle: …" | A required top-level member is missing, an unknown one was added, or the receipt member does not hold a receipt. |
INVALID_ENVELOPE | — | An object in the bundle does not have the structure of a K-Block object. |
TOO_LARGE | — | More than 1,024 objects, 1,024 identity documents or 1,024 disclosures. |
This release reads bundle format 0.1 with Protocol v0.1 objects only, and it is the only format K-Block Cloud issues. A different version means the file was edited or was not produced by this K-Block release. See Limits and supported versions.
Fix. Look at the first members of the file: a K-Block bundle starts with "kblock_bundle": "0.1" and has the members receipt, objects and identities. Download the bundle again if you are not sure the copy is intact.
In the dashboard, Verify a receipt file accepts files up to 16 MiB. A larger file is refused before verification with "The file is larger than 16384 KiB."
An object is missing from the bundle
A receipt commits to an exact set of objects: the signed steps on the path from the TASK to the receipt's head, the delegations behind them and their revocations, attestations about those steps, and the identity document of every signer. The verifier compares the bundle with that commitment.
| What is missing or extra | What you see |
|---|---|
| A covered object, for example the APPROVAL | Receipt coverage INVALID with RECEIPT_MISMATCH "covered objects differ (missing: […], uncovered: [])". Signature chain and the later checks show SKIPPED. |
| An object the receipt does not cover | The same check: RECEIPT_MISMATCH with the extra IDs under uncovered, or "bundle covers objects unrelated to the head's path". |
| The identity documents of a signer | UNKNOWN_IDENTITY "no identity documents for pinned kid:kb:…" under Receipt coverage. When the issuer's own documents are missing, the receipt's signature cannot be checked and Signature chain fails instead. |
| A pinned identity document replaced by another | RECEIPT_MISMATCH "…: receipt pins identity document …, bundle's latest is …" under Receipt coverage. |
For example, an excerpt of the text report of a bundle whose APPROVAL was removed (the removed_approval case of the verifier's test vectors):
Bundle format: VALID
Identity chains: VALID
Signature chain: SKIPPED
Receipt coverage: INVALID
↳ covered objects differ (missing: ["ko_xvrzn2u4xjn335vs2qlml4pfnrgygngyy…
Graph integrity: SKIPPED
Workflow & authorization: SKIPPED
Receipt summary: SKIPPED
The interaction details are not shown because the receipt did not verify.
Errors:
RECEIPT_MISMATCH [ko_jer2b6…ng7q] covered objects differ (missing: ["ko_xvr…
Note: Values ending in … are shortened; the JSON report (--json) has them in full.
RESULT: INVALIDCause. The file was put together or edited by hand, cut short, or merged from several bundles. A bundle downloaded unchanged from K-Block Cloud is complete.
Fix. Download the bundle again and verify the downloaded file without changing it.
The Cloud API cannot serve the bundle
Every time a receipt page loads and every time a bundle is downloaded, K-Block Cloud reads the stored bundle and checks it against the SHA-256 recorded at issuance. It never serves a bundle that fails this check. Because the receipt page is built from the bundle, the whole page fails with "Could not load the receipt"; a failure of Verify in this browser or Download .kbundle shows "Could not fetch the bundle".
When K-Block cannot read the stored receipt file (the file is missing, or K-Block's receipt storage failed), or the file no longer matches its recorded hash (it was changed or damaged, so K-Block refuses to serve it), the error says "K-Block could not complete this request because of a problem on its side." It goes on "If this keeps happening, contact K-Block support and give them the request ID below." and shows the Request ID. The Cloud API answers HTTP 500.
One case shows a code:
| Code | HTTP | Message | Cause |
|---|---|---|---|
INVALID_STATE | 409 | "the bundle is stored in the '…' backend, not '…'" | A problem on K-Block's side: K-Block cannot currently read this receipt's file from where it was stored. |
Fix. Nothing in the dashboard, the SDK or the CLI repairs a stored bundle. Reload the page later; if the error continues, contact K-Block support and give them the receipt ID and the request ID shown in the error (and the code, for INVALID_STATE). Tell them also whether every receipt fails, or only the receipts of executed calls while those of rejections and cancellations still load: that difference helps K-Block find the cause.
MISMATCH on a receipt page
MISMATCH means the downloaded bundle verifies, but its receipt, thread, organization or action is not the one the page shows. The notice The bundle does not match this receipt lists each difference, for example "The verified organization (…) does not match the organization this page shows (…)."
This should not happen with a bundle the Cloud API serves for the page. Do not rely on the bundle as this receipt, and contact K-Block support with the receipt ID and the notice.
The two dashboard verdicts disagree
A receipt page shows two verdicts side by side. They answer different questions:
| Verification by the Cloud | Independent check in this browser | |
|---|---|---|
| Runs | On the Cloud API, on the stored bundle | In your browser, on the downloaded bundle |
| Gateway pin | Not applied | Applied: VALID only for the pinned gateway |
| Identity documents | The bundle's, plus those K-Block stores for every signer | The bundle's, plus the pinned gateway's current documents |
| Attesters trusted | Every K-Block-aware MCP server registered in the organization | Only those you select under Trust attestations from |
| Also fails when | A signer's identity has equivocated (published two conflicting identity documents) | The bundle's SHA-256 differs from the recorded hash |
- Cloud valid, browser CONSISTENT or NOT THIS GATEWAY. Expected when the pin is missing or wrong: only the browser applies it. See The receipt is not from your gateway.
- Cloud invalid, browser VALID. The Cloud API knows identity documents the bundle does not contain, for example a later key-compromise report or an equivocation. Open Raw reports, then Cloud verification report: its
errorsandequivocated_identitiessay why. Do not rely on the receipt until K-Block support has explained it: contact them with the receipt ID and the Cloud verification report.
Report recorded at issuance, also under Raw reports, is the verification made when the receipt was issued, for comparison.
Warnings and notes on a valid receipt
Warnings and notes do not make a receipt invalid. They tell you what the verification could not establish.
| Finding | Meaning | What to do |
|---|---|---|
| Note "Key freshness not checked for N of M signers…" | The signers' identity documents came only from the bundle, so a key-compromise report published after issuance would not be seen. The dashboard supplies the pinned gateway's current documents, so it usually reports the other signers only. | Pass current identity documents obtained outside the bundle: --identities <file> in the CLI, knownIdentities in the SDK. |
KEY_LATER_COMPROMISED | A signer's key was reported compromised after the time the step claims. Times are asserted by the signer, not by a trusted clock. | Treat the step with caution and ask the signer when the key was compromised. A key reported compromised before the step's time fails the receipt with KEY_REVOKED. |
IDENTITY_UPDATED_SINCE_RECEIPT | A signer has published a newer identity document since the receipt pinned its document; the objects were checked against the newer one. | Usually nothing, for example after a key rotation. |
IDENTITY_DOCUMENT_IGNORED | An identity document you supplied is not part of the signer's authenticated chain and was ignored. | Check where the document came from. |
| Attestation "not trusted by this verifier" | The upstream server's attestation verified, but you did not choose to trust its attester. | Select the server under Trust attestations from, or pass its KID with --trust or trustedAttesters, if you trust it. |
The text report (the CLI's and renderText's) also ends with the same reminder every time: K-Block proves who signed what, in which order and under which authority, not that real-world effects occurred unless a trusted attester signed that claim. That is a limit of every receipt, not a finding about this one.
Command-line errors
The kblock verify command exits with 0 for a valid receipt, 1 for an invalid one and 2 when it cannot reach a verdict. Exit code 2 comes with a line error: … on standard error, for example when:
- the bundle file cannot be read;
- a
--trustvalue is not a valid KID (kid:kb:followed by the base32 digest); - an
--identitiesfile cannot be read, is not strict JSON, or holds something other than a K-Block object or an array of them; - a
--workflowdefinition cannot be read or registered.
The CLI does not compare the issuer with your gateway: check the Issued by: line yourself. See Verify a receipt for how to run it and its options.