Skip to main content
This is the reference for every code an extension can be handed. Each carries a stable code and a sentence you can act on. Errors from the SDK and the bridge are TapError; a failure your own server returned reaches your component as a TapServerError carrying your own code and message; Tappify’s own refusals arrive with the codes in the runtime, publish and load-failure tables below.
TapError.is and TapServerError.is are the narrowing you want: both check the error’s own name and code rather than instanceof, so they hold across a bundle boundary.

In your component

tap.actions.run resolves with the run Tappify created and rejects with a TapServerError carrying one of the ACTION_* codes below when Tappify refused to create one.

In your build

On your server

createTappifyHandler answers these as JSON with the code and the sentence, so you rarely construct one yourself.

In your tests

Runtime errors from Tappify

These arrive on a bridge call. The host turns a SCOPE_NOT_GRANTED into TAP_SCOPE_MISSING before your component sees it; the rest reach you with the code below. None of the eight assistant codes reaches a component of yours, and none of them is shown to the owner as an error. What each one leaves behind differs:
  • A TOOL_* refusal ends one tool call. The assistant is told the tool did not answer and carries on with the turn, and the call is recorded against the install as a tool_call with an error outcome.
  • CONTEXT_NOT_DECLARED, or a TOOL_FAILED on the context route, drops that context block in silence: the assistant is told nothing, and the call is recorded as a context_call with an error outcome.
  • A MENTION_* refusal costs the owner the items from that extension in the @ picker. It never reaches the assistant, because the picker is the owner’s own, and no runtime row is written.
  • PROMPT_NOT_DECLARED costs the prompt_sent record, not the turn: the owner’s question is answered as they sent it, and no runtime row is written.
The two kinds of row that are written are counted by type on your Runtime page, and listed with their outcome in the owner’s install activity.

Publish and account errors

Load failures

When a bundle cannot be mounted, the host draws its failure card and an owner can send the reason to your Runtime page. These are the codes that reach you there.

What the CLI shows

A failed command prints what failed, why, and the fix, and never a stack. For most codes the “why” is the sentence Tappify returned. The fix is a command where one exists — tappify login, tappify vendor create, tappify extension set id, tappify extension doctor and tappify extension publish are the ones it names most often — and a sentence where the fix is not yours to run, such as asking a vendor admin or asking Tappify support. A code with no tailored fix falls back to tappify extension doctor. Add --json to any tappify extension or tappify vendor command and the same failure arrives as { "ok": false, "error": { "code", "what", "why", "fix" } }.

Writing your own

The code is stable and the message is what your component shows the owner, so write a sentence that says what happened and what to do. A TapServerError your handler throws reaches the extension with your own code and message intact; anything else your handler throws reaches it as TAP_INTERNAL_ERROR with Tappify’s sentence, and the detail stays in your logs.

Scopes

What TAP_SCOPE_MISSING and SCOPE_NOT_GRANTED are asking you to declare.