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 the tokens selected by the source patterns in
test/guide-tokens.test.mjs, grouped by where you meet them, with the cause
and the way out. A test in this repository
(test/guide-tokens.test.mjs) compares the token headings with tokens matched
by its configured source patterns, which omit some emitted tokens, including
client_elicitation_unsupported, receipt_correlation_capacity_exceeded,
and receipt_signer_absent.
Refusal output includes the four 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> - from a
sealcommand 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.
While using the protected tool
Section titled “While using the protected tool”Minted in contract/contract.cjs and spine/proxy.cjs; delivered as the
tool’s error result. 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.
already_consumed
Section titled “already_consumed”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.
declined
Section titled “declined”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.
terminally_declined
Section titled “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.
cancelled
Section titled “cancelled”You answered Cancel (rather than Decline) at the prompt, and a later retry referenced that cancelled request. Make a fresh call.
expired
Section titled “expired”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.
restart_invalidated
Section titled “restart_invalidated”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.
unknown_state
Section titled “unknown_state”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.
state_malformed
Section titled “state_malformed”The retry’s requestState is not even the shape of a handle Seal issues.
Same standing as unknown_state.
arguments_altered
Section titled “arguments_altered”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.
tool_altered
Section titled “tool_altered”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.
context_mismatch
Section titled “context_mismatch”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.
response_malformed
Section titled “response_malformed”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.
authorization_disagreement
Section titled “authorization_disagreement”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.
runtime_tree_fail
Section titled “runtime_tree_fail”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.
runtime_tree_unknown
Section titled “runtime_tree_unknown”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.
kernel_integrity_refused
Section titled “kernel_integrity_refused”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.
kernel_manifest_refused
Section titled “kernel_manifest_refused”The runtime manifest is missing, unreadable, malformed, or lacks a valid WASM pin. Restore a complete pinned installation; do not invent a replacement hash.
kernel_execution_refused
Section titled “kernel_execution_refused”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.
kernel_output_refused
Section titled “kernel_output_refused”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.
unrenderable_effect
Section titled “unrenderable_effect”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.
project_server_drifted
Section titled “project_server_drifted”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.
state_absent
Section titled “state_absent”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.
protected_server_missing
Section titled “protected_server_missing”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).
protected_server_failed
Section titled “protected_server_failed”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.
forward_refused
Section titled “forward_refused”A defensive fallback: a pre-forward check refused without naming a token.
The shipped checks always name one (project_server_drifted,
state_absent), so meeting this token would itself be worth reporting.
Running seal protect and seal unprotect
Section titled “Running seal protect and seal unprotect”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.)
project_server_absent
Section titled “project_server_absent”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.
project_server_invalid
Section titled “project_server_invalid”.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 missing
command). The message names the specific problem; fix the file.
project_environment_missing
Section titled “project_environment_missing”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.
project_environment_unsupported
Section titled “project_environment_unsupported”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.
project_server_non_stdio
Section titled “project_server_non_stdio”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.
protected_server_start_failed
Section titled “protected_server_start_failed”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.
protected_server_initialize_failed
Section titled “protected_server_initialize_failed”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.
protected_server_tools_list_failed
Section titled “protected_server_tools_list_failed”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.
protected_server_tools_empty
Section titled “protected_server_tools_empty”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.
protected_tool_absent
Section titled “protected_tool_absent”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.
claude_unavailable
Section titled “claude_unavailable”The claude command is not on PATH. seal protect installs the gate
through Claude Code, so it needs it. Install Claude Code or fix PATH,
then re-run.
local_override_exists
Section titled “local_override_exists”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.
local_override_unreadable
Section titled “local_override_unreadable”Seal could not read or parse the local Claude Code configuration; the refusal names the underlying error, and no configuration was changed.
local_override_drifted
Section titled “local_override_drifted”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.
no_seal_owned_override
Section titled “no_seal_owned_override”Seal has no stored ownership proof for this server’s local override, so it will not remove or reinterpret that override.
server_required
Section titled “server_required”Several server records exist, so seal recover --archive cannot choose one.
Run seal recover --archive SERVER for the intended incompatible record.
duplicate_server_state
Section titled “duplicate_server_state”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.
already_protected
Section titled “already_protected”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.
active_claude_session
Section titled “active_claude_session”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.
lease_generation_mismatch
Section titled “lease_generation_mismatch”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.
claude_install_failed
Section titled “claude_install_failed”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.
claude_remove_failed
Section titled “claude_remove_failed”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.
incompatible_state
Section titled “incompatible_state”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.
recovery_not_needed
Section titled “recovery_not_needed”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.
recovery_state_invalid
Section titled “recovery_state_invalid”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.
recovery_state_changed
Section titled “recovery_state_changed”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.
proxy_lease_active
Section titled “proxy_lease_active”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.
installation_lock_active
Section titled “installation_lock_active”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.
installation_lock_invalid
Section titled “installation_lock_invalid”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.
activation_state_changed
Section titled “activation_state_changed”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.
process_witness_unavailable
Section titled “process_witness_unavailable”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.
drifted
Section titled “drifted”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.
state_broken
Section titled “state_broken”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.
protected_tool_vanished
Section titled “protected_tool_vanished”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.
From seal doctor
Section titled “From seal doctor”elicitation_hook_configured
Section titled “elicitation_hook_configured”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.
duplicate_member
Section titled “duplicate_member”The receipt JSON repeats an object member name. Duplicate names make the meaning parser-dependent, so the checker refuses before validation.
unexpected_member
Section titled “unexpected_member”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.
number_not_canonical
Section titled “number_not_canonical”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.
value_not_canonical
Section titled “value_not_canonical”Canonicalisation encountered a value outside JSON’s null, boolean, string, supported finite-number, array, and object forms. The checker does not coerce it.
inert_input
Section titled “inert_input”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.
Platform and version refusals
Section titled “Platform and version refusals”unsupported_platform
Section titled “unsupported_platform”Printed by the installer, the installed launcher, and the demo alike for Seal v0.4.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.
node_missing
Section titled “node_missing”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.
version_mismatch
Section titled “version_mismatch”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.
From the installer
Section titled “From the installer”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.
install_parent_unwritable
Section titled “install_parent_unwritable”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.
install_target_unwritable
Section titled “install_target_unwritable”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.
existing_install_untrusted
Section titled “existing_install_untrusted”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.
macos_helper_absent
Section titled “macos_helper_absent”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.
macos_helper_architecture
Section titled “macos_helper_architecture”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.
pin_missing
Section titled “pin_missing”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.
pin_invalid
Section titled “pin_invalid”The --sha256 value is not 64 lowercase hex characters. Copy the published
digest exactly.
unknown_flag
Section titled “unknown_flag”The installer takes only --sha256, --prefix, and --bytes; anything
else is refused rather than ignored.
artifact_malformed
Section titled “artifact_malformed”The file is not a built release artifact — no payload, a damaged header, or trailing bytes. Re-download it.
artifact_truncated
Section titled “artifact_truncated”The artifact ends before its payload does (an interrupted download, as exercised by cutting a real artifact short). Re-download it.
artifact_digest_mismatch
Section titled “artifact_digest_mismatch”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.
artifact_missing
Section titled “artifact_missing”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.
artifact_unreadable
Section titled “artifact_unreadable”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.
launcher_digest_mismatch
Section titled “launcher_digest_mismatch”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.
launcher_unreadable
Section titled “launcher_unreadable”The installed launcher cannot read its own file. Check its file permissions or restore a complete pinned installation.
install_record_missing
Section titled “install_record_missing”lib/seal/install.json is gone from the install prefix. The launcher
refuses to guess what should be in the store. Reinstall.
install_record_unreadable
Section titled “install_record_unreadable”The install record exists but is not readable JSON (exercised by corrupting it). Reinstall.
install_record_malformed
Section titled “install_record_malformed”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.
From the receipt checker
Section titled “From the receipt checker”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.
read_failed and duplicate_member
Section titled “read_failed and duplicate_member”The received bytes are not an unambiguous UTF-8 JSON document. Truncation, ill-formed UTF-8, and duplicate names at any depth refuse here.
member_order and number_not_canonical
Section titled “member_order and number_not_canonical”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.
commitment_mismatch
Section titled “commitment_mismatch”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.
input_mismatch and inert_input
Section titled “input_mismatch and inert_input”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.
signature_mismatch
Section titled “signature_mismatch”The signature is malformed or does not verify under the caller-supplied key. A receipt-embedded key is never trusted as authority.
verdict_mismatch
Section titled “verdict_mismatch”Replaying the exact recorded inputs through the WASM kernel produced a different verdict. The receipt does not establish the decision it records.
action_verdict_mismatch
Section titled “action_verdict_mismatch”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.
Up: Guide. Next: What is protected right now.