Skip to Content
WhatsApp MCPAPIErrors

Error catalogue — Meta codes + our typed errors

src/utils/meta-errors.ts maps Meta Cloud API integer error codes to typed JS error classes. Tool handlers pattern-match on the typed class; audit logging (Phase 4+) groups failures by class for alerting.

Mapped Meta codes

Meta codeTyped classWhenRecovery
131047OutOfSessionWindowErrorSending a plain text message outside the 24h customer-service windowUse a pre-approved template (send_template, deferred to Phase 8)
131026InvalidRecipientErrorRecipient phone not on WhatsApp / malformedVerify to was normalised through toMetaFormat; if so, the recipient is genuinely unreachable
132xxxTemplateErrorTemplate render / language / status problemsRe-check template body, parameter list, and language code; see incident runbook for approval recovery
190InvalidAccessTokenErrorMeta access token invalid or expiredRotate via the incident runbook (access_token_secret_ref resolves through the secrets layer, so rotation = swap one file + container restart)
anyMetaError (base)Catch-allInspect .code, .subcode, .fbtraceId and consult Meta’s error code reference 

Our internal errors

PhoneNormalisationError

Thrown by src/utils/phone.ts when input is empty or yields fewer than 8 digits after stripping non-digit characters. Always surfaces at the tool boundary — the LLM sees a clear “phone must contain at least 8 digits” message and retries with the corrected input.

How tools surface errors

  • Validation error (zod) — MCP protocol error code -32602 (InvalidParams). The dispatcher in src/server/mcp.ts handles it.
  • Typed Meta error — thrown from the handler; the dispatcher wraps it as { isError: true, content: [...] } so the LLM gets a tool-level error result, not a protocol failure. This lets the model decide to retry with different args, surface a clean explanation to the user, or escalate.
  • Out-of-window (131047) — handler throws Error('Cannot send a plain text message — the 24h customer-service window has elapsed. ...'). Same wrapping; the message is the explanation.
  • Internal invariant failures — thrown as plain Error. Logged at warn/error; the model gets isError: true with the message.

HTTP-level errors (Phase 5+ remote MCP)

Reserved here for future content. Phase 5 lands the Streamable HTTP transport.

Rate-limit errors (Phase 4+)

LayerShape
HTTP429 Too Many Requests + Retry-After, X-RateLimit-* headers
MCP protocolJSON-RPC error -32004 with data: { retryAfterSeconds, scope }

Surfacing rate-limit failures as a protocol error (not a tool error) signals to the model that it should back off, rather than retry the tool with different args.