Skip to content

When something looks wrong

Seal prints tokened refusals — a token is a fixed snake_case name for what happened — and un-tokened errors such as seal: usage: seal verify PATH. This page lists refusal tokens grouped by where you meet them, with the cause and the way out. test/guide-tokens.test.mjs checks the original level-three entries and also derives runtime keys from source to check the added level-four entries.

Refusal output includes the five shapes below, plus un-tokened seal: <message> errors, seal: REFUSE <token>: <message> protection errors, and REFUSED <token> lines with a separate message when supplied:

  • as the protected tool’s error result in Claude Code: approval refused: <token> — <detail>
  • as a JSON-RPC error when the wrapper refuses a message before any tool decision: seal proxy: <token>: <detail>
  • from a seal command on stderr: seal: <token>: <message>
  • from the wrapper as Claude Code starts the protected server (visible in Claude Code’s MCP logs): seal __proxy: <token>: <message>
  • as a plain refusal line from the installer, the installed launcher, the checker, or seal doctor: REFUSE <token>: <reason>

A refusal means Seal did not complete the action at the point named by that token. It does not prove an earlier step changed nothing: for example, an installer or a failed external command can have made partial changes first.

Minted in contract/contract.cjs and spine/proxy.cjs; delivered as the tool’s error result, except method_not_allowed, key_case_fold_collision, case_variant_name and number_not_representable, which arrive as a JSON-RPC error. The first group is the approval contract judging a retry; unless a token says otherwise, the way to proceed is simply to make a fresh call and approve it fresh.

You saw: approval refused: already_consumed — this one-use approval has already been consumed. The same approval was presented a second time; the call did not run again. This is the replay protection holding. If you wanted the effect twice, call twice and approve twice. If you never approved a first time, read the receipts before doing anything else.

You answered Decline at the prompt. The call did not run, and this request can never be revived by re-sending the same approval — see terminally_declined.

A retry arrived for a request you already declined. Denial is terminal per request: nothing you declined can be re-approved later by replaying it. A fresh call gets a fresh prompt.

You answered Cancel (rather than Decline) at the prompt, and a later retry referenced that cancelled request. Make a fresh call.

More than 2 minutes passed between Seal issuing the approval handle and evaluating the retry, so the window closed. Exercised for real: after a 125-second wait the retry was refused with expired — the approval window closed before the retry arrived. Approval expiry follows the local wall clock. Call again and answer within the window.

The approval prompt was issued before a restart (of Claude Code, and with it the wrapper), and the answer arrived after. Pending approvals deliberately do not survive a restart. Call again in the new session.

The retry carried an approval handle Seal never issued — an altered handle and a never-issued one are indistinguishable by design, because the handle carries no information to tamper with. Make a fresh call. If this happens without anything unusual on your side, something rewrote the client’s traffic; that is worth investigating.

The retry’s requestState is not even the shape of a handle Seal issues. Same standing as unknown_state.

The retried call’s arguments differ from the exact arguments shown at the prompt — exercised for real by slipping an extra argument into an approved call. The approval covers the exact effect you saw, so the changed call was refused. If the new arguments are what you want, call with them and approve what the prompt then shows.

Defensive: the retry names a different tool than the one the approval was bound to. In the shipped gate only the guarded tool’s calls reach the contract, so this token was not reachable in our runs; it exists so the binding is enforced in depth.

Defensive, like tool_altered: the retry claims a different project or server than the approval was bound to. Not reachable through the shipped gate, which pins both.

The approval answer did not have the expected shape — for example, an accept with no readable approve value. Usually a client defect rather than a user action. Call again; if it repeats, the client is not returning the form faithfully.

Node and the WASM kernel gave different answers to an authorization row. The detail names the side that refused. Seal fails closed and does not consume or forward the call. Preserve the receipt and report the disagreement; retrying without understanding it is not a remedy.

The same token refuses a call to a selected tool that Node would forward without approval because no argument predicate matched, when the kernel blocks that call. The kernel re-checks the predicates its match language can express (exact strings, booleans, safe integers and prefix-only patterns on undotted argument names) and its own checks of the call’s JSON, such as a lone surrogate escape or nesting deeper than it accepts.

The installed files no longer match the fixed install record, an unrecorded file appeared, or the record changed after this wrapper started. The approval was refused. Reinstall and restart the protected session. Matching disk bytes at a check does not establish which bytes the process already loaded.

No trustworthy installed-tree record was available at the approval check. Missing or unreadable records and source-checkout wrappers cannot authorize protected calls. Complete a verified installation and restart the session; the wrapper never creates or adopts a replacement record during approval.

The vendored WASM is missing, unreadable, or its SHA-256 does not match the published pin. Seal does not fall back to JavaScript authorization. Restore the installed artifact from bytes that match the trusted distribution pin.

The runtime manifest is missing, unreadable, malformed, or lacks a valid WASM pin. Restore a complete pinned installation; do not invent a replacement hash.

Protect or proxy startup found no operator public-key pin, or the signed policy did not match the selected tools and pin. Obtain a signed guard and forward policy from the operator. Set SEAL_KERNEL_POLICY_FILE and SEAL_KERNEL_PUBKEY before Protect. An old protected state needs a new Protect operation with the signed policy.

The authorization adapter received no valid public-key pin. The call does not forward. Protect the route with an operator-signed policy and an independent public-key pin, then restart the protected client.

The isolated kernel worker could not start, exceeded its 30000ms execution deadline, seal_init rejected its signed configuration, or kernel execution failed. Nothing forwards. A deadline refusal includes the exceeded limit in milliseconds, which distinguishes a hung worker from other execution failures. Preserve the full detail and report it; this may expose a slow or damaged runtime, or an incompatible kernel/config boundary.

The kernel worker returned unreadable output or a verdict other than ALLOW or BLOCK. Seal treats silence and malformed output as denial. Preserve the output detail and report it.

Seal refused to ask for approval because the effect exceeded Seal’s own rendering rules — an argument line was too wide, the message needed more lines than Seal permits, or an argument had no canonical form (a non-finite number or one outside the supported numeric range). Decimals such as 1.5, coordinates, and scientific notation are supported. Passing those rules does not guarantee that the client paints every message-body line: in the recorded Claude Code 2.1.251 dialog it folds three of six lines and still paints the Accept button. The current schema description carries every argument, the full scope with TTL, and the outside-Seal boundary in the channel that recording paints; the new layout is not yet recorded. Seal does not take the protected server down to refuse a value the receipt writer will not seal. The tool call is refused; nothing ran. If you control the arguments, make the complete message fit the display limits; otherwise this tool’s calls cannot be interactively approved.

The server’s .mcp.json entry changed while the gate was up, so forwarding stopped — for the guarded tool and everything else on that server. seal status will show DRIFTED; the ways out are on the status page.

The protected server’s .mcp.json entry can no longer be read as a valid launch configuration. The refusal includes the specific reason, such as an args member that is not a string. Seal does not forward the call or record this as drift. Fix the file, stop the current Claude Code session, then unprotect and protect the server again.

The recorded protection state file disappeared while the wrapper was running (exercised by removing it mid-session). Seal fails closed: nothing forwards without the record. If you deleted Seal’s data directory, seal unprotect refuses with no_seal_owned_override because its ownership proof is gone; stop Claude Code, inspect the server’s local override and remove it manually only after confirming it is the Seal wrapper, then protect again; if you did not delete the directory, find out what did.

The protected server’s command no longer exists — its file was deleted or moved since protect. Seen at wrapper start as seal __proxy: protected_server_missing: protected server command is missing: <path>, or mid-session as a tool refusal. Restore the server (or unprotect, fix .mcp.json, and protect again).

The protected server’s process failed to start for a reason other than the command being missing (permissions, for example). Not reached in our runs — the failure modes we provoked surfaced as the server exiting instead. Fix whatever stops the server starting; the gate refuses to forward until it does.

A defensive fallback: a pre-forward check refused without naming a token. The shipped checks always name one (project_server_drifted, project_server_malformed, state_absent), so meeting this token would itself be worth reporting.

The wrapper forwards to the protected server only the MCP methods on its fixed list (FORWARDED_METHODS in spine/proxy.cjs: the lifecycle, ping, tools, resources, prompts and completion methods a client uses) and refuses any other method by name with method_not_allowed. The match is exact, so tools/invoke, TOOLS/CALL, tools/call with a trailing space, a look-alike character, an empty method and a method that is not a string are all refused. A request gets a JSON-RPC error naming the method; a notification gets no response. Both write a BLOCK receipt, and nothing reaches the server.

One object in the message held two keys that are equal under Unicode case folding, such as path and Path. The check covers the message envelope, params, and every object inside a tools/call’s arguments and _meta. Some servers match keys case-insensitively and would read a different value than the one Seal judged, so nothing was forwarded. Send each key once.

An envelope key, tools/call params key or tool name equals a name Seal acts on only under case folding: for example Method, Arguments, or Write_File when write_file is protected. A method spelled that way is refused as method_not_allowed instead. Nothing was forwarded. Use the exact spelling.

A tools/call carried a number that Seal cannot forward unchanged. Seal forwards every tools/call rebuilt from its parsed fields, so a literal that overflows a double, or an integer with more precision than a double holds (such as 9007199254740993), would reach the server as a different value. Send such a value as a string.

Minted in spine/protection.cjs; ProtectionError instances print as seal: REFUSE <token>: <message>, while ownership refusals print REFUSED <token> with a separate message when supplied.

Arguments missing: seal protect SERVER TOOL and seal unprotect SERVER need their names. (Running seal protect without arguments prints seal: REFUSE usage: usage: seal protect [--timeout-ms MILLISECONDS] SERVER TOOL[?ARG=SCALAR|?ARG~"PATTERN"] [TOOL...] and exits with code 1.)

There is no .mcp.json in the project, or it has no server by the name you gave. Run in the project directory, and spell the server exactly as .mcp.json does.

.mcp.json exists but could not be used: not valid JSON, or the named server entry is malformed (a non-array args, a non-object env, a non-string cwd, or a missing command). The message names the specific problem; fix the file.

A launch field in .mcp.json references an unset environment variable without a fallback. Set the named variable in the environment used to run Seal and Claude, or provide ${VAR:-default}, then retry. Seal refuses to launch the literal placeholder. Resolved launch values are bound into protection’s drift detection; changing them requires unprotecting and protecting the server again.

A launch field contains a placeholder outside Seal’s supported ${VAR} and ${VAR:-default} syntax. Use one of those forms or a literal value, then retry. Expansion happens once against the launching environment, not against other entries in the server’s env object. A set but empty variable stays empty.

The named server is http (or anything but stdio). Seal gates local stdio servers only — there is no local process to stand in front of otherwise.

Seal could not start the configured server while checking its tools, or the server exited before initialization began. Check the command, permissions, environment, and any stderr included in the message, then run seal protect again. The same check runs when the wrapper activates; a failure there marks the protection state BROKEN.

The configured server did not complete the MCP initialize exchange: it may have timed out, exited, rejected the request, or returned malformed output. Fix the server or increase the discovery limit with --timeout-ms, then run seal protect again. At wrapper activation this failure marks the state BROKEN.

The configured server did not return a usable, complete tools/list: it may have timed out, exited, rejected the request, returned malformed output, or repeated a pagination cursor. Fix the server or increase the discovery limit with --timeout-ms, then run seal protect again. At wrapper activation this failure marks the state BROKEN.

The server’s complete tools/list contained no named tools, so there is nothing Seal can guard. Fix the server’s tool registration and protect again. At wrapper activation this failure marks the state BROKEN.

The server answered tools/list, but the tool named on seal protect was not among the observed names. Check the spelling or fix the server so it exposes that tool, then protect again.

Before protect, unprotect or recovery changes state, Seal requires the resolved claude --version command to exit zero and name Claude Code on stdout or stderr. The refusal reports the resolved path, output and exit status. Fix PATH to select a working Claude Code installation, then retry. On WSL, select the Linux installation rather than a Windows shim that exits silently.

The claude command became unavailable when Seal checked for a local override (claude command is not available). A command missing from PATH at the initial check instead prints seal: REFUSE claude_unusable: Claude Code command "claude (not found on PATH)" failed identification. seal protect installs the gate through Claude Code, so it needs it. Install Claude Code or fix PATH, then re-run.

Claude Code already has a local-scope override for this server name — maybe an earlier experiment, maybe something else using the same mechanism. Seal will not overwrite it. Look at it with claude mcp get <server>; remove it with claude mcp remove --scope local <server> if it is yours to remove.

Seal could not read or parse the local Claude Code configuration; the refusal names the underlying error, and no configuration was changed.

Seal read the local Claude Code configuration and found that the server’s local override is not the definition Seal installed. Restore that definition before trusting status or asking Seal to remove the override.

Seal has no stored ownership proof for this server’s local override, so it will not remove or reinterpret that override.

Several server records exist, so seal recover --archive cannot choose one. Run seal recover --archive SERVER for the intended incompatible record.

The same server has both a legacy project record and a new server record. Seal refuses to choose between two state histories. Stop the wrappers and resolve which record and installed override are authoritative before retrying; Seal has not copied, deleted or combined either history.

The requested server already has recorded protection (the message names the server and its state, e.g. server "db" is already PENDING RESTART). To change that server’s tool set, unprotect it first. Protecting another server in the same project leaves the first server protected.

seal unprotect found a live Claude Code session still running the wrapper. Stop that session first. Taking the gate down under a live session is exactly the kind of silent change Seal exists to prevent.

The proxy’s durable lease generation changed while it was evaluating an approval. Seal rechecks the proxy’s lease immediately before consuming the approval and refuses without consuming it if that check fails, but after that check passes and the approval is consumed, a lease mismatch at the subsequent pre-forward check refuses the forward with the approval’s one use already spent.

The claude mcp add step reported failure, and Seal recorded the state as BROKEN with that reason. Fix the external cause, then retry seal protect if no local override was installed. Seal checks the actual local configuration and refuses retry while a wrapper lease is live.

If the command installed the Seal override before reporting failure, stop Claude Code and run seal unprotect <server> before protecting again. Unprotect checks the actual override against the recorded ownership definition, including for older failed-install records. A foreign replacement or unreadable local configuration still refuses; inspect that configuration and Claude Code’s error before making manual changes. An absent failed override has nothing to remove, so use the protect retry path.

Unprotect can remove an owned, inactive override even if the project’s .mcp.json is missing or invalid JSON. Its output reports the source observation before and after removal. Readable files have byte hashes; unavailable files have null hashes, which do not establish that unreadable contents are unchanged.

The claude mcp remove step failed during unprotect — most simply because Claude Code could not remove the local override. Stop Claude Code and inspect the actual override and Claude Code’s error before making any manual change. Do not create an arbitrary replacement: Seal removes only the installed override whose definition matches its recorded ownership proof.

The recorded state has a schema this Seal binary cannot interpret. Seal accepts the supported seal.protect/v1 spellings, including older single-tool records; the creating binary’s sealVersion is provenance, not a compatibility gate. Unsupported schemas still refuse.

Seal includes seal recover. It requires exactly one argument, --archive.

The current binary also provides an explicit recovery command: stop Claude Code and run seal recover --archive in the affected project. It preserves the exact incompatible state at the printed archive path before removing Seal’s owned local override. The route is then outside Seal; review the archived server, tools and predicates and run seal protect SERVER TOOL [TOOL...] with your chosen selections. Restart Claude Code and run seal status to check activation. Recovery works locally without downloading another binary, using the installed Claude CLI when an override needs removal.

seal recover --archive found compatible state or no state. Recovery is only for incompatible state; it made no state or configuration changes. Run seal status to inspect the current route. To remove compatible protection, stop Claude Code and run seal unprotect SERVER.

The incompatible state does not establish this project’s identity and server name. Recovery refuses to guess ownership and made no state or configuration changes. Inspect the recorded state and the actual Claude Code local override before deciding what manual repair is needed; do not invent ownership metadata.

The stored state changed while recovery was in progress. Stop Claude Code and other commands changing this project’s protection, then retry seal recover --archive. The archive already written is retained. Recovery may have removed the owned override before detecting the change, so inspect the actual override and state before restarting the client.

As Claude Code starts the protected server

Section titled “As Claude Code starts the protected server”

Printed by the wrapper on stderr, visible in Claude Code’s MCP logs as seal __proxy: <token>: <message>. protected_server_missing and incompatible_state (above) can also appear here.

Another Seal proxy already owns this project’s protected route. The second starter is refused with the holder pid and lease generation. Stop that session, or let it exit and retry; a crashed owner is recoverable when its PID and process-start witness are no longer live and the next holder takes the next generation. The same token also names a project lock held by another Seal operation: a seal protect, seal unprotect or seal recover run, or another wrapper’s own record check or lease commit. That refusal says retry after that operation finishes; tool discovery runs outside the lock, and a starting wrapper waits up to 3200ms for each lock acquisition before refusing. At that bound the message names a lock-acquisition timeout and the unfinished operation, which may be waiting for a slow subprocess; it does not establish that another session owns a live lease. This is a transient start event, not a persisted project status.

Another Seal operation holds this installation’s lifecycle lock. Startup waits up to 3200ms for each lock acquisition, then refuses with a message naming the installation lock and its live holder PID. Let that operation finish and retry. This does not mean another wrapper owns the route’s session lease.

Seal cannot validate or acquire the installation’s lifecycle lock. A stale owner, malformed lock record or unavailable process-start witness requires inspection; startup does not remove the lock or wait out this refusal. Check the named lock and operation before retrying. A live PID with a different process-start witness is stale, even if that PID still exists.

For a stale or invalid owner record, the refusal prints Recovery command: followed by rm -- and the exact shell-quoted lock path. Stop all Seal operations, inspect and recheck that lock, then run the printed command and retry. The command can remove a replacement live lock; Seal never runs it automatically.

The stored protection record changed while the wrapper was discovering the server’s tools: the server was unprotected, or the command, environment, .mcp.json digest, journal, receipts directory, discovery timeout or protected selections it was validated against are no longer the ones stored. Discovery runs outside the project lock, so such a change is possible, and it is refused at the lease commit: no lease was taken and nothing was written. Run seal status, then restart Claude Code so a new wrapper validates the current record. This is a transient start event, not a persisted project status.

Seal raises this token when a protected state operation cannot establish a process-start witness for a live process. The error message names the specific situation and states the correct remedy. The approval-journal lock path runs on any platform, including macOS. On Linux x86-64, Seal reads /proc/<pid>/stat; it refuses with process_witness_unavailable if it cannot read that file, if the stat record is malformed, or if the process-start field is absent. On macOS x64/arm64, the stored lease and project-lock path uses the native process-start witness helper. That path reports helper or witness failures with Darwin-specific refusal tokens.

The .mcp.json server entry changed between sessions, discovered at wrapper start: seal __proxy: drifted: project server drifted before proxy activation. The server does not start; seal status shows DRIFTED. Ways out are on the status page.

The recorded state file exists but cannot be read (exercised by corrupting it). Seal will not gate on a record it cannot read. If you have no explanation for the damage, treat that seriously; the blunt recovery is to remove the broken state and the local override and protect again.

The guarded tool existed at protect time but was absent from tools/list when the wrapper tried to activate. Seal marks the protection state BROKEN instead of starting a gate for a different tool set. Restore the tool, then remove the broken state and local override and protect again.

seal doctor found an auto-response hook configured (SEAL_ELICITATION_AUTO_RESPONSE or CLAUDE_ELICITATION_AUTO_RESPONSE), so approval prompts in this environment may be answered by software, not by you. No approval should be trusted until the hook is removed.

The receipt JSON repeats an object member name. Duplicate names make the meaning parser-dependent, so the checker refuses before validation.

The signature object contains one or more members that the checker does not allow. The checker refuses the receipt and names every member it did not allow; the signature object must contain exactly algorithm and value. Remove every named member and obtain a new valid receipt before you run the checker again.

The receipt contains a non-finite number or a value outside [-9007199254740991,9007199254740991]. Decimals are supported within that range and retain their parsed JSON numeric value. Integer-only fields, such as timestamps, keep their own validation rules.

Canonicalisation encountered a value outside JSON’s null, boolean, string, supported finite-number, array, and object forms. The checker does not coerce it.

The receipt supplies non-empty grants or forecasts, which the current kernel does not consume. They must be empty so signed-but-ignored input cannot appear to affect replay.

Receipt refusals use the same tokens whether you invoke the installed seal verify command or the standalone v2 checker. The producer, command, and checker all use seal.receipt/v2; there is no second receipt format to select.

Printed by the installer, the installed launcher, and the demo alike for Seal v1.0.0. Seal supports install, demo and receipt checking on Linux x86-64 and macOS x64/arm64. Protect is supported on Linux x86-64 and macOS x64/arm64; macOS Protect execution is not exercised in CI. Windows, Linux ARM and other unsupported installations refuse without changing files.

The install artifact could not find node on PATH. Seal requires Node 20 or newer. Seal supports install, demo and receipt checking on Linux x86-64 and macOS x64/arm64. Protect is supported on Linux x86-64 and macOS x64/arm64; macOS Protect execution is not exercised in CI.

Two version records that must agree do not — the installed VERSION file against the install record, or package.json against VERSION in a build. A healthy install never shows this; it means a mixed or hand-edited installation. Reinstall from a fresh artifact.

The installer checks the artifact before it runs it, but some later checks occur while extraction is writing the store. A refusal here can therefore follow partial store writes; remove the failed install and reinstall from a verified artifact. Minted in scripts/install.cjs.

Seal could not create, replace, or clean up an installer target because its parent directory is not writable. Check that the selected prefix and its bin and lib/seal parents are owned and writable by the installing user; then repair those permissions or choose a different prefix and rerun the verified artifact.

Seal found an existing install target but cannot inspect or read it because the installing user lacks permission. Do not force past that boundary: repair the target’s ownership and read permission if it is your install, or select a fresh prefix that you control and install there.

The selected prefix already contains incomplete, non-regular, unreadable, or non-verifying Seal install targets, or store content that differs from the artifact being installed. Do not overwrite it blindly. Choose a fresh prefix, or repair the existing install only after determining why it no longer verifies; then rerun the verified artifact.

A Darwin artifact build did not receive the native process-start witness helper from its matching release runner. No artifact was packed. Compile the helper on that runner and pass its path with --macos-helper.

The supplied native process-start witness helper is not a thin Mach-O for the Darwin artifact’s named architecture. No artifact was packed. Compile it on the matching runner and check the helper against that platform before retrying.

You ran the artifact without --sha256. The pin is required, on purpose: you state the digest of the bytes you meant to install, from where you got them, and the installer confirms it.

The --sha256 value is not 64 lowercase hex characters. Copy the published digest exactly.

The installer takes only --sha256, --prefix, and --bytes; anything else is refused rather than ignored.

The file is not a built release artifact — no payload, a damaged header, or trailing bytes. Re-download it.

The artifact ends before its payload does (an interrupted download, as exercised by cutting a real artifact short). Re-download it.

The artifact’s bytes do not match the --sha256 pin, or an inner file does not match the payload manifest. Either the download is corrupt or the pin is for a different artifact. From the installed launcher, the same token means an installed file no longer matches the install record — the store has been modified since install (exercised by editing one installed file). Reinstall.

A file the record promises is not there — the installer’s payload lacks a required file, or (from the launcher) an installed file has been deleted from the store. Reinstall.

An installed file exists but cannot be read (permissions, for example). The launcher refuses to run a store it cannot fully judge. Fix the permissions or reinstall.

From the installed seal command at startup

Section titled “From the installed seal command at startup”

Before running anything, the installed launcher checks the whole store against its install record. The artifact_* tokens above are its refusals too. Minted in scripts/seal-launch.cjs.

The installed launcher’s bytes differ from its entry in lib/seal/install.json. Restore a complete pinned installation; do not change the recorded digest to match the changed file.

The installed launcher cannot read its own file. Check its file permissions or restore a complete pinned installation.

lib/seal/install.json is gone from the install prefix. The launcher refuses to guess what should be in the store. Reinstall.

The install record exists but is not readable JSON (exercised by corrupting it). Reinstall.

The install record’s store path points outside the install prefix. This should never occur from a genuine install; treat the installation as untrustworthy and reinstall from a verified artifact.

The canonical meaning of the four v2 verbs is in Receipt operations. The checker was landed before the producer and does not import the producer’s assembler or canonicaliser. See the normative v2 contract for the full refusal boundary.

The received bytes are not an unambiguous UTF-8 JSON document. Truncation, ill-formed UTF-8, and duplicate names at any depth refuse here.

The envelope does not use the fixed v2 top-level order, or a number is not a finite number in the supported range. Object members inside values use ECMAScript own-property enumeration order after parsing: integer-index keys come first in ascending numeric order, followed by other string keys in insertion order.

The arguments or kernel configuration no longer hash to the recorded replay commitment. Keep the receipt as tamper evidence and do not rely on it.

The granted capabilities do not exactly match the approval targets, or a reserved input channel was populated even though the current kernel does not consume it.

The supplied Ed25519 receipt verification key is a small-order point. Such a key can accept forged signatures, so the checker refuses it before checking the signature. Obtain the receipt signer’s ordinary public key from a trusted source; retrying with the same key cannot establish a valid signature.

The supplied Ed25519 receipt verification key encodes a y coordinate at or above 2^255 - 19. The checker refuses this noncanonical encoding before checking the signature. Obtain the signer’s public key from a trusted source; do not reduce or rewrite the supplied key to make verification pass.

The signature is malformed or does not verify under the caller-supplied key. A receipt-embedded key is never trusted as authority.

Replaying the exact recorded inputs through the WASM kernel produced a different verdict. The receipt does not establish the decision it records.

The receipt’s action is missing, empty, or not a string. The checker requires a non-empty string before checking signatures or replaying the recorded inputs. Preserve the malformed receipt and report it to its producer.

The receipt’s signed action says ALLOW, but replaying its recorded kernel inputs does not produce ALLOW. The checker refuses the receipt rather than reporting a decision the kernel did not make. Preserve the receipt and report the mismatch.

The receipt has no signature; obtain a signed receipt from its producer. The checker and seal verify keep the UNVERIFIED table, print REFUSE signature_absent: receipt has no signature, and exit 1.

No usable public key was supplied; pass the separately obtained signer key with --pubkey (64 lowercase hexadecimal characters). The checker and seal verify keep the UNVERIFIED table, print REFUSE public_key_absent: no usable public key supplied to check the signature, and exit 1.

These entries cover source paths beyond the original level-three guide inventory.

From seal __demo-grant-server: a grant ID has already been spent for the same grant. Use a new grant ID for a new effect; do not replay the spent one.

From the bootstrap installer: the download exceeded its published byte length. Discard it and check the release asset before retrying.

From seal __demo-grant-server: the signed grant names a different audience. Request a grant for this target audience.

From the bootstrap installer: embedded release data or its artifact name is malformed. Download a fresh bootstrap script from the release page; do not edit it.

From seal __proxy: the client cannot answer elicitation requests. Use a client with elicitation support before retrying the protected call.

From seal __proxy: the client lacks form elicitation support. Use a client that supports form elicitation.

From seal __demo-grant-server: the clock interval is invalid or untrusted. Restore a trustworthy clock and restart the demonstration target.

From seal __demo-grant-server: the grant was consumed but readiness failed before the effect started. Inspect target health and issue a fresh grant; do not replay this one.

From seal demo --dir: the chosen directory lies inside the real receipt store. Choose a separate demonstration directory.

From the bootstrap installer: the release download failed or was interrupted. Check the network and release URL, then retry.

From seal __demo-grant-server: the requested effect has an invalid tool, argument or receipt hash. Send a valid demo.mutate request and obtain a matching grant.

From seal __demo-grant-server: the effect hash differs from the signed grant. Request a grant for the exact effect you intend.

From seal __demo-grant-server: the grant JSON differs from its canonical encoding. Have the grant issuer encode and sign the canonical grant.

From seal __demo-grant-server: the grant expires beyond the permitted 120-second window. Request a short-lived grant.

From the bootstrap installer: --prefix has no value. Supply a prefix path or omit the flag.

From seal __demo-grant-server: a spent grant ID belongs to different grant bytes. Issue a grant with a fresh ID; investigate the conflicting issuer record.

From seal __demo-grant-server: the supplied grant cannot be parsed within its bounds. Request a correctly encoded grant from its issuer.

From seal __demo-grant-server: the call has no grant member. Supply the issued grant with the call.

From seal __proxy: protected state lacks its project or server identity. Stop the client and recreate protection with seal protect.

From the bootstrap installer: the verified downloaded installer could not start. Check executable permissions and host support, then retry from a verified asset.

From seal verify --json: the command arguments are invalid. Follow seal verify PATH [--pubkey HEX] [--json] and supply one path.

From seal verify --json: receipt validation failed without a more specific code. Preserve the receipt and inspect its schema against the v2 reference.

From seal __demo-grant-server: the enrolled key does not cover the audience, profile or tool. Use an issuer enrolled for this scope.

From seal __demo-grant-server: the grant issuer key is absent or unsuitable. Enroll the intended Ed25519 issuer key.

From seal __demo-grant-server: the issuer key was revoked or changed before execution. Obtain a new grant from an active enrolled key.

From seal protect on macOS: the native helper could not establish a plausible boot time. Check the installed helper and machine clock before retrying.

From seal protect on macOS: the native witness helper is missing or is not a regular file. Repair the installation before protecting.

From seal protect on macOS: the native witness helper lacks execute permission. Repair the installed helper permissions.

From seal protect on macOS: the helper identity changed across a witness check. Stop and inspect the installed helper before retrying.

From seal protect on macOS: the helper could not establish a plausible process identity. Repair the helper or investigate its output, then retry.

From the bootstrap installer: Node is older than the release minimum. Upgrade Node to the version named in the refusal and retry.

From seal __demo-grant-server: the effect counter or append failed after grant consumption. Inspect the target and its output before deciding whether to issue a new grant.

From seal __demo-grant-server: the grant uses an unsupported profile. Ask the issuer for the demonstration execution-grant profile.

From seal __proxy: too many receipt correlations are outstanding. Allow pending calls to finish before sending more.

From seal protect or seal __proxy: the receipt key path is not a directory. Move the conflicting path and retry key creation.

From seal protect or seal __proxy: the receipt key directory is not mode 0700. Set its mode to 0700 after confirming ownership.

From seal protect or seal __proxy: the receipt key directory cannot be created or inspected. Fix its parent permissions and retry.

From seal protect or seal __proxy: a receipt key file is empty. Restore the original key pair from a trusted backup or create a new pair deliberately.

From seal protect or seal __proxy: creating the receipt signing pair failed. Fix the key directory or storage problem, then retry.

From seal protect or seal __proxy: only one of the private and public receipt key files exists. Restore the matching missing file or create a new pair deliberately.

From seal protect or seal __proxy: a receipt key has invalid bytes or format. Restore a valid matching pair; do not edit key bytes.

From seal protect or seal __proxy: the public receipt key does not match the private key. Restore the matching pair before signing.

From seal protect or seal __proxy: a receipt key path is not a regular file. Replace it with the intended regular key file.

From seal protect or seal __proxy: a key file has the wrong mode. Use mode 0600 for the private key and 0644 for the public key.

From seal protect or seal __proxy: a receipt key cannot be inspected or read. Check file ownership and read permissions.

From seal __proxy: receipt assembly has no signer. Repair receipt-key loading before making protected calls.

From seal __proxy: a receipt value is undefined. Inspect the producer of the missing value and retry after correcting it.

From seal __proxy: a receipt value has an unsupported type or number. Use canonical JSON values within the supported numeric range.

From seal __demo-grant-server: the request or its frame has an invalid shape. Send a well-formed demo.mutate request.

From seal __demo-grant-server: the target files or counter cannot be verified. Repair the demonstration target files before retrying.

From seal verify --json: the local kernel runtime is unavailable. Repair the installed runtime and retry verification.

From seal __demo-grant-server: the grant fields or types violate its schema. Request a valid grant from its issuer.

From seal __demo-grant-server: the issuer signature does not verify. Obtain a correctly signed grant from the enrolled key.

From seal verify --json: receipt validation completed, but its signature or another verification check did not pass. Supply the correct public key for a signed receipt, or obtain a valid signed receipt before retrying.

From seal __demo-grant-server: the grant store changed or is still inside startup quarantine. Inspect the store identity and wait for readiness before retrying.

From seal __demo-grant-server: a grant tombstone could not be created. Repair store access before issuing another grant.

From seal __demo-grant-server: a grant tombstone is unreadable or its write may be partial. Preserve the store for inspection and do not replay the grant.

From seal __demo-grant-server: the target configuration or lock is not ready. Repair the target and restart it before issuing a grant.

Up: Guide. Next: What is protected right now.