Voice failures show on three different planes. The plane tells you where to fix the failure:
  • Control-plane APIoriginate, transfer, hangup and the other control calls run over tRPC to the control-plane API. Failures come back as an HTTP status plus a VoiceError in the SDK. Fix these in your integration code or credentials.
  • The call itself — a call that reaches the carrier can still end unanswered, busy, or rejected. This outcome shows after the fact on the CDR as a coarse status plus a raw Q.850 cause on the terminal cleared event. It does not show as an exception on your originate call.
  • Transport & tools — the media/data plane (WebTransport/MoQT) and any agent tool calls fail independently of the control plane. Connection state reports transport failures. Structured tool-error envelopes report tool failures.
When originate returns success, the engine accepted the request and started to dial. Success does not mean that the far end answered. Watch the call lifecycle events (initiated → delivered → established → cleared) or the CDR status to learn the outcome. See Call lifecycle.

Call-failure causes

Every call ends with a cleared lifecycle event. The event carries a Q.850 cause and a talk duration. The CDR pipeline folds these into a coarse status. The reports and CDR views read this status. The rule is simple: any call with talk time > 0 is completed. The cause labels a zero-duration call.
The CDR builder derives status. duration_sec > 0 always wins and marks the call completed, even for cause 16 with a very short talk time. The Q.850 cause classifies only zero-duration calls. Unmapped causes fall through to failed. The CDR keeps the raw q850_cause (0 when unknown), so you can see the exact carrier cause behind a coarse failed.
The CDR is the durable record of the outcome. For each cleared call you get the coarse status, the raw q850_cause, the direction, ANI/DNIS, the trunk, and the talk duration. When the call had engine-anchored media, you also get per-call QoS: packet loss, jitter, and an E-model MOS.
A codec/SDP mismatch on the trunk is a recurring cause of 488-flavored failed calls (for example, a single-codec answer that the carrier rejects). These calls fail at INVITE time. They never reach established. They clear with a non-standard cause. See Call not connecting.

Auth errors

Control-plane calls authenticate with your API key and workspace (org) id. The SDK validates required inputs before it sends any request. If an input is missing, the SDK throws a VoiceError. If the API rejects a request, the SDK shows the server’s HTTP status in the same VoiceError.
SIP-side auth is a separate surface. Trunk and softphone REGISTER/INVITE run through the SIP gateway’s internal registrar. The registrar uses multidomain digest auth and a source-IP ACL. A rejected credential returns 401/403 on the SIP dialog, not a VoiceError. An INVITE from a source that is not on the allowlist gets a 403 Forbidden answer. When STIR/SHAKEN policy is set to reject, an INVITE with an invalid attestation gets a 438 Invalid Identity Header answer. A common trunk-registration gotcha: un-seal sealed credentials before the digest computation, or the carrier reports “Digest auth failed.” See SIP trunking and SIP trunk troubleshooting.

Transport errors

The media/data plane is QUIC/HTTP-3 with MoQT over WebTransport on a single :443. Transport failures are connection-state transitions, not thrown exceptions. The client auto-reconnects with capped backoff. The client also replays your publishes and subscribes. Treat a transient Reconnecting as normal.
The QUIC→WebSocket fallback ladder ships only in the native SDK core (Python, Go, Rust, Java, .NET). The browser/TypeScript SDK is WebTransport-only today. The browser has no automatic WS fallback. The browser SDK deliberately throws when encoded transform is missing; it does not silently degrade. This is a known gap, not a misconfiguration. See Browser audio troubleshooting.
For one-way or missing audio (a call that connected but has no media), the fault is usually media-plane, not control-plane. See One-way audio.

Tool errors

When an agent calls a tool, the runtime does not throw failures. The runtime returns a structured error envelope to the model as the tool result. The agent can then say “I can’t do that right now” or fall back to another tool. Every tool error has the same shape:
string
The human-readable failure reason. This field is present only on failure. A successful tool returns its raw result body (an empty result is normalized to {}).
HTTP and MCP tool timeouts are bounded: the runtime clamps the timeout_ms that you set to [100ms, 60000ms]. A tool that hangs returns the timeout envelope. The tool does not stall the turn.
Client-side tools are not yet plumbed. With a tool declared as type: "client", the runtime asks the connected SDK client to execute the tool. This currently returns a not yet plumbed error envelope on every invocation. The client-tool wire channel has not shipped. Client tools are preview. Use http or mcp tools for anything that you need to work today. HTTP tools, MCP tools, and the telephony transfer tool are shipped. See Agent tools.

Handling patterns

1

Separate acceptance from outcome

Treat a successful originate as “dialing started.” Decide success from the lifecycle events or the CDR status. Never decide success from the absence of an exception.
2

Branch on the coarse status, drill on Q.850

Route your retry logic on status. busy and no-answer are often retryable; rejected usually is not. Log the raw q850_cause, so you can later split a bucket of failed calls by their real cause.
3

Catch VoiceError at the control-plane boundary

Wrap control calls and inspect err.message. The SDK folds both client-side validation and the server HTTP status into one VoiceError. A 401/403 in the message is an auth/entitlement problem, not a call problem.
4

Treat transport state as advisory

Let the client auto-reconnect. Show an error to your user only after a sustained Failed. Remember that the browser SDK does not fall back to WebSocket.
5

Feed tool errors back to the model

The runtime already returns tool failures as { "error": … } to the agent. You generally do not need to intercept them. Monitor them in the agent traces to find a flaky endpoint or a client tool that is still preview.

Call lifecycle

The initiated → delivered → established → cleared event flow that carries the Q.850 cause.

Call not connecting

Diagnose INVITE-time failures, codec/SDP mismatches, and trunk rejects.

SIP & RTP debugging

Pull SIP traces and per-call QoS to see the real cause behind a failed call.

Agent tools

How you declare and invoke HTTP, MCP, client, and telephony tools.