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 aSCOPE_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 atool_callwith an error outcome. CONTEXT_NOT_DECLARED, or aTOOL_FAILEDon the context route, drops that context block in silence: the assistant is told nothing, and the call is recorded as acontext_callwith 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_DECLAREDcosts theprompt_sentrecord, not the turn: the owner’s question is answered as they sent it, and no runtime row is written.
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
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.