Skip to content

Error, refusal and event codes ​

Every stable code Toolmark produces, with the milestone that introduced it. Codes are part of the public contract: new codes may be added in minor releases, existing ones are never renamed.

  • refused codes appear in a ToolResult ({ status: 'refused', code, message, rev? }), so agents see them. See Results.
  • error event codes appear on tm.events.on('error', (e) => …) and createToolmark({ onError }) as { code, message, tool?, cause? }. They are for the app's developers and logs, never for the agent.
  • Thrown codes: in development (createToolmark({ dev: true })) misconfiguration throws a ToolmarkError whose code is the same string as the production error event (spec §14).

refused codes ​

CodeMilestoneWhen
unknown_toolM1No visible tool has that name (never registered, disposed, or its scope is hidden by when: false). Carries the current rev. A tool removed while its call waited in the scope queue also ends with this code.
staleM1The call named a rev older than the registry's and the tool no longer exists (carries rev); or a deferred form/wizard submit was approved after the values changed ("Form changed since confirmation was requested", M1 execution ruling).
not_allowedM1Policy does not let this caller use the tool; an inline-mode caller has no confirm handler for a consequential tool; a DOM button is disabled or hidden (M2); a stepwise wizard fill with no mounted step form (M2).
busyM1The tool's scope queue is full (calls run serially per scope, D22).
undo_unavailableM1tm.undo(callId) found no restorer: the call registered none, it was already undone, it was evicted from the capped undo store, or its tool is gone.
confirmation_expiredM1tm.confirmPending(confirmId, …) with an unknown, expired, already-consumed or dropped (scope disposed) confirmId. A confirmId is single use.
file_rejectedM2A file field value was refused: { ref } with no files.resolve (with the files_not_configured event), a disallowed URL, size/type/count limit, or a resolver failure (spec §8.4; see Files).
navigation_failedM2The Inertia navigation tool could not complete the visit (see Inertia).

Related statuses that are not refused: cancelled with by: 'operator' | 'signal' | 'policy', invalid with issues, and error with a generic message (a thrown tool never leaks its error text to the agent). Over the bridge, an unknown protocol number is answered with { status: 'error', message: 'unsupported protocol' }; a missing result after the server's deadline is mapped to a timeout on the server (spec §12.2).

error event codes ​

CodeMilestoneWhenDevelopment / production
duplicate_nameM1A second tool with the same full name or the same llmName; M2: server-declared props tool collisions and a second inertiaPagesThrows / event, second registration rejected (props: event in both)
invalid_nameM1A tool or scope name outside A–Z a–z 0–9 _ - ., or with an empty segment; M2: invalid DOM tool, group, column or field names and buttons without a descriptionThrows / event, not registered
invalid_scopeM1register(tool, { scope }) with a scope object from another registryThrows / event, not registered
scope_disposedM1Registering into a disposed scopeThrows / event, not registered
invalid_policyM1A malformed policy: an entry for human, an unknown caller or hint classThrows / event
invalid_confirm_modeM1confirmMode sets anything other than 'inline' for webmcp, mcp or tourThrows / event
missing_confirm_handlerM1A consequential/destructive tool that no allowed caller can confirm (thrown/rejected); or, as a development-only warning, one hidden from inline-mode callers without a handlerThrows / event; warning variant dev only
schema_conversion_failedM1No JSON Schema could be derived for a tool's input; M2: fromJsonSchema rejected a schema, or a DOM field's pattern was droppedThrows / event (fromJsonSchema always throws)
tool_budget_exceededM1More visible tools than ToolmarkOptions.budget (default 40, D21)Event, development only
tool_threwM1A tool's run threw (the caller gets a generic error result); M2: an options provider failed, or registering a DOM tool threwEvent in both
output_invalidM1A tool's ok data fails its output schema (the result is kept)Event, development only
late_resultM1A tool settled after its call was abandoned (abort grace period); the result is droppedEvent in both
ctx_confirm_unavailableM1ctx.confirm() ran for a caller with no inline confirmation path (it resolved { approved: false, reason: 'confirmation_unavailable' })Event, development only
transport_failedM1A bridge transport failed to send, a result was not serializable, or a call rejected unexpectedlyEvent in both
invalid_messageM1An inbound bridge message was dropped: too large (maxMessageBytes), not serializable, or failing the protocol v1 schemaEvent in both
invalid_pathM1A form path with a forbidden segment (__proto__, prototype, constructor) reached the path helpersThrown ToolmarkError only
files_not_configuredM1 / M2A { ref } file value arrived and no files.resolve exists. M1 threw a ToolmarkError; since M2 it is an event next to refused file_rejectedEvent, development only
wizard_misconfiguredM2A wizard with no steps, an invalid or duplicate step name, or missing callbacksThrows / event, nothing registered
files_misconfiguredM2Invalid files options or FileFieldSpecThrows / event
invalid_props_toolM2A server-declared props entry is malformed or exceeds a limit; the entry is skippedEvent in both, never thrown
options_url_rejectedM2A data-tool-options-url is invalid, too long, not same-origin http(s), or has credentialsEvent in both, never thrown
wizard_current_step_unsyncedM2A wizard wrote the current step into parent data with no way to show itEvent in both (once per wizard)
webmcp_unavailableM3Informational, once per consumer: no model context (no native API and no polyfill installed one)Event in both
webmcp_register_failedM3registerTool rejected or a filter threw; reported once per tool version, retried when the tool changesEvent in both

confirmation_unavailable, signal and expired are reason values of a rejected ConfirmOutcome ({ approved: false, reason }), not event codes.

Desktop MCP (M3) ​

WebSocket close codes between toolmark-mcp and the page: 4400 (invalid pairing frame), 4401 (wrong code or unknown token), 4408 (no pairing frame within 3000 ms) and 4409 (superseded) are terminal; 4429 (handshake busy), 1001 (CLI shutting down) and 1009 (frame too large) are transient. Details: Desktop MCP.

Tour events (M4) ​

Delivered to tour.on((e) => …) (see Tours):

EventreasonWhen
step_entered—The tour moved to a step (index is set)
step_invalidunknown_tool, unknown_param, malformed_stepA planned step was dropped: its tool is not visible to caller tour, its param is not in the tool's input schema, or it is not a step object
anchor_missinganchor_missingThe step's element is not rendered; the step was skipped
step_skippedtool_removedThe step's tool disappeared mid-tour
done—Every step completed

startTour rejects with a TypeError for invalid options, and with the planner's own error when the planner rejects.

toolmark lint (M4) ​

Exit codes: 0 no errors, 1 errors found, 2 usage or runtime failure (bad arguments, an invalid manifest file, --url without @playwright/test or without the page test hook, a judge module that cannot be found).

Rule idSeveritySource
name-formaterrorbuilt-in
description-missingwarnbuilt-in
description-shortwarnbuilt-in
param-description-missingwarnbuilt-in
schema-invaliderrorbuilt-in
llm-name-collisionerrorbuilt-in
tool-budgetwarnbuilt-in
consequential-hint-missingerrorbuilt-in
options-without-hintwarnbuilt-in
judge-failedwarnany --judge that threw
judge/description-qualitywarn@toolmark/judge-typesafe
judge/consequential-hintwarn (error with strictHints)@toolmark/judge-typesafe
judge/overlapwarn@toolmark/judge-typesafe
judge/overlap-truncatedwarn@toolmark/judge-typesafe
judge/unavailablewarn@toolmark/judge-typesafe
judge/timeoutwarn@toolmark/judge-typesafe

Rule definitions: Lint.