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
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, 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.
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.
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.
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_config_refused
Section titled “kernel_config_refused”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.
kernel_config_pin_absent
Section titled “kernel_config_pin_absent”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.
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.
project_server_malformed
Section titled “project_server_malformed”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.
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,
project_server_malformed, state_absent), so meeting this token would
itself be worth reporting.
method_not_allowed
Section titled “method_not_allowed”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.
key_case_fold_collision
Section titled “key_case_fold_collision”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.
case_variant_name
Section titled “case_variant_name”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.
number_not_representable
Section titled “number_not_representable”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.
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 non-string
cwd, or 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_unusable
Section titled “claude_unusable”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.
claude_unavailable
Section titled “claude_unavailable”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.
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.
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.
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 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.
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.
public_key_small_order
Section titled “public_key_small_order”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.
public_key_noncanonical
Section titled “public_key_noncanonical”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.
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.
invalid_action
Section titled “invalid_action”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.
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.
signature_absent
Section titled “signature_absent”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.
public_key_absent
Section titled “public_key_absent”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.
Additional runtime refusal entries
Section titled “Additional runtime refusal entries”These entries cover source paths beyond the original level-three guide inventory.
already_spent
Section titled “already_spent”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.
artifact_oversized
Section titled “artifact_oversized”From the bootstrap installer: the download exceeded its published byte length. Discard it and check the release asset before retrying.
audience_mismatch
Section titled “audience_mismatch”From seal __demo-grant-server: the signed grant names a different audience. Request a grant for this target audience.
bootstrap_malformed
Section titled “bootstrap_malformed”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.
client_elicitation_unsupported
Section titled “client_elicitation_unsupported”From seal __proxy: the client cannot answer elicitation requests. Use a client with elicitation support before retrying the protected call.
client_form_elicitation_unsupported
Section titled “client_form_elicitation_unsupported”From seal __proxy: the client lacks form elicitation support. Use a client that supports form elicitation.
clock_untrusted
Section titled “clock_untrusted”From seal __demo-grant-server: the clock interval is invalid or untrusted. Restore a trustworthy clock and restart the demonstration target.
consumed_not_started
Section titled “consumed_not_started”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.
demo_directory_is_real_receipt_store
Section titled “demo_directory_is_real_receipt_store”From seal demo --dir: the chosen directory lies inside the real receipt store. Choose a separate demonstration directory.
download_failed
Section titled “download_failed”From the bootstrap installer: the release download failed or was interrupted. Check the network and release URL, then retry.
effect_invalid
Section titled “effect_invalid”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.
effect_mismatch
Section titled “effect_mismatch”From seal __demo-grant-server: the effect hash differs from the signed grant. Request a grant for the exact effect you intend.
encoding_noncanonical
Section titled “encoding_noncanonical”From seal __demo-grant-server: the grant JSON differs from its canonical encoding. Have the grant issuer encode and sign the canonical grant.
expiry_too_far
Section titled “expiry_too_far”From seal __demo-grant-server: the grant expires beyond the permitted 120-second window. Request a short-lived grant.
flag_missing
Section titled “flag_missing”From the bootstrap installer: --prefix has no value. Supply a prefix path or omit the flag.
grant_id_conflict
Section titled “grant_id_conflict”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.
grant_malformed
Section titled “grant_malformed”From seal __demo-grant-server: the supplied grant cannot be parsed within its bounds. Request a correctly encoded grant from its issuer.
grant_missing
Section titled “grant_missing”From seal __demo-grant-server: the call has no grant member. Supply the issued grant with the call.
identity_absent
Section titled “identity_absent”From seal __proxy: protected state lacks its project or server identity. Stop the client and recreate protection with seal protect.
installer_launch_failed
Section titled “installer_launch_failed”From the bootstrap installer: the verified downloaded installer could not start. Check executable permissions and host support, then retry from a verified asset.
invalid_arguments
Section titled “invalid_arguments”From seal verify --json: the command arguments are invalid. Follow seal verify PATH [--pubkey HEX] [--json] and supply one path.
invalid_receipt
Section titled “invalid_receipt”From seal verify --json: receipt validation failed without a more specific code. Preserve the receipt and inspect its schema against the v2 reference.
issuer_scope_refused
Section titled “issuer_scope_refused”From seal __demo-grant-server: the enrolled key does not cover the audience, profile or tool. Use an issuer enrolled for this scope.
key_not_enrolled
Section titled “key_not_enrolled”From seal __demo-grant-server: the grant issuer key is absent or unsuitable. Enroll the intended Ed25519 issuer key.
key_revoked
Section titled “key_revoked”From seal __demo-grant-server: the issuer key was revoked or changed before execution. Obtain a new grant from an active enrolled key.
macos_boot_time_unavailable
Section titled “macos_boot_time_unavailable”From seal protect on macOS: the native helper could not establish a plausible boot time. Check the installed helper and machine clock before retrying.
macos_helper_missing
Section titled “macos_helper_missing”From seal protect on macOS: the native witness helper is missing or is not a regular file. Repair the installation before protecting.
macos_helper_not_executable
Section titled “macos_helper_not_executable”From seal protect on macOS: the native witness helper lacks execute permission. Repair the installed helper permissions.
macos_helper_substituted
Section titled “macos_helper_substituted”From seal protect on macOS: the helper identity changed across a witness check. Stop and inspect the installed helper before retrying.
macos_process_witness_failed
Section titled “macos_process_witness_failed”From seal protect on macOS: the helper could not establish a plausible process identity. Repair the helper or investigate its output, then retry.
node_too_old
Section titled “node_too_old”From the bootstrap installer: Node is older than the release minimum. Upgrade Node to the version named in the refusal and retry.
outcome_unknown
Section titled “outcome_unknown”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.
profile_unsupported
Section titled “profile_unsupported”From seal __demo-grant-server: the grant uses an unsupported profile. Ask the issuer for the demonstration execution-grant profile.
receipt_correlation_capacity_exceeded
Section titled “receipt_correlation_capacity_exceeded”From seal __proxy: too many receipt correlations are outstanding. Allow pending calls to finish before sending more.
receipt_key_directory_invalid
Section titled “receipt_key_directory_invalid”From seal protect or seal __proxy: the receipt key path is not a directory. Move the conflicting path and retry key creation.
receipt_key_directory_permissions
Section titled “receipt_key_directory_permissions”From seal protect or seal __proxy: the receipt key directory is not mode 0700. Set its mode to 0700 after confirming ownership.
receipt_key_directory_unreadable
Section titled “receipt_key_directory_unreadable”From seal protect or seal __proxy: the receipt key directory cannot be created or inspected. Fix its parent permissions and retry.
receipt_key_empty
Section titled “receipt_key_empty”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.
receipt_key_generation_failed
Section titled “receipt_key_generation_failed”From seal protect or seal __proxy: creating the receipt signing pair failed. Fix the key directory or storage problem, then retry.
receipt_key_incomplete
Section titled “receipt_key_incomplete”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.
receipt_key_invalid
Section titled “receipt_key_invalid”From seal protect or seal __proxy: a receipt key has invalid bytes or format. Restore a valid matching pair; do not edit key bytes.
receipt_key_mismatch
Section titled “receipt_key_mismatch”From seal protect or seal __proxy: the public receipt key does not match the private key. Restore the matching pair before signing.
receipt_key_not_regular
Section titled “receipt_key_not_regular”From seal protect or seal __proxy: a receipt key path is not a regular file. Replace it with the intended regular key file.
receipt_key_permissions
Section titled “receipt_key_permissions”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.
receipt_key_unreadable
Section titled “receipt_key_unreadable”From seal protect or seal __proxy: a receipt key cannot be inspected or read. Check file ownership and read permissions.
receipt_signer_absent
Section titled “receipt_signer_absent”From seal __proxy: receipt assembly has no signer. Repair receipt-key loading before making protected calls.
receipt_value_absent
Section titled “receipt_value_absent”From seal __proxy: a receipt value is undefined. Inspect the producer of the missing value and retry after correcting it.
receipt_value_malformed
Section titled “receipt_value_malformed”From seal __proxy: a receipt value has an unsupported type or number. Use canonical JSON values within the supported numeric range.
request_malformed
Section titled “request_malformed”From seal __demo-grant-server: the request or its frame has an invalid shape. Send a well-formed demo.mutate request.
resource_unavailable
Section titled “resource_unavailable”From seal __demo-grant-server: the target files or counter cannot be verified. Repair the demonstration target files before retrying.
runtime_unavailable
Section titled “runtime_unavailable”From seal verify --json: the local kernel runtime is unavailable. Repair the installed runtime and retry verification.
schema_invalid
Section titled “schema_invalid”From seal __demo-grant-server: the grant fields or types violate its schema. Request a valid grant from its issuer.
signature_invalid
Section titled “signature_invalid”From seal __demo-grant-server: the issuer signature does not verify. Obtain a correctly signed grant from the enrolled key.
signature_unverifiable
Section titled “signature_unverifiable”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.
store_quarantine
Section titled “store_quarantine”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.
store_unavailable
Section titled “store_unavailable”From seal __demo-grant-server: a grant tombstone could not be created. Repair store access before issuing another grant.
store_uncertain
Section titled “store_uncertain”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.
target_unready
Section titled “target_unready”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.