VoiceThere

Session errors & error events

VoiceThere records structured session errors in the dashboard (last 20 per project) and emits matching session_error events on the voice-control data channel. Browser and Node clients use onSessionError from @voicethere/client/browser; agent bundles can implement errorHook in @voicethere/agent.

Error event envelope (data channel)

New payloads use type: "session_error". Legacy agent_error JSON is still accepted by the client and mapped to AGENT_CHILD_CRASHED.

{
  "type": "session_error",
  "code": "AGENT_CHILD_CRASHED",
  "message": "Sorry, something went wrong.",
  "session_id": "<orchestrator-session-id>",
  "project_id": "<uuid>",
  "build_id": "<uuid>",
  "stack": "Error: …\n  at …",
  "recoverable": false,
  "customer_context": { "userId": "u_123" },
  "occurred_at": "2026-06-19T12:00:00.000Z"
}

Error code catalog

All codes are defined in SESSION_ERROR_CODES (platform, runner, client). Sources stored in the database: agent, runner, provisioning.

Runner / agent (remote — on voice-control)

CodeWhen
AGENT_HANDLER_FAILEDAgent handler threw; errorHook ran; runner ending session
AGENT_CHILD_CRASHEDChild process exit or unhandled bundle load failure
RUNNER_INTERNALRunner-side failure (e.g. crash TTS could not play)
SESSION_END_FAILEDSession marked failed on teardown
IDLE_TIMEOUT_CALLBACK_FAILEDAgent onIdleTimeout hook threw
IDLE_TIMEOUT_CALLBACK_TIMED_OUTonIdleTimeout exceeded 30s grace
SESSION_IDLE_TIMEOUTInformational — peer idle limit reached (often paired with session_close)

Client-only (local — never on data channel)

Emitted by @voicethere/client to onSessionError before or without a live WebRTC leg.

CodeWhen
PROVISIONING_FAILEDSession start job failed or HTTP error
PROVISIONING_TIMEOUTAsync provisioning poll exceeded timeout
WEBRTC_CONNECTION_FAILEDPeer connection state became failed
WEBRTC_CONNECTION_CLOSEDUnexpected close before graceful disconnect
WEBRTC_CONNECT_TIMEOUTwaitForConnected timed out

Full enum (12 codes): AGENT_HANDLER_FAILED, AGENT_CHILD_CRASHED, RUNNER_INTERNAL, SESSION_END_FAILED, IDLE_TIMEOUT_CALLBACK_FAILED, IDLE_TIMEOUT_CALLBACK_TIMED_OUT, SESSION_IDLE_TIMEOUT, PROVISIONING_FAILED, PROVISIONING_TIMEOUT, WEBRTC_CONNECTION_FAILED, WEBRTC_CONNECTION_CLOSED, WEBRTC_CONNECT_TIMEOUT.

@voicethere/agent — errorHook

Optional hook in defineAgent runs in the sandboxed child when handler code throws, before the runner plays crash TTS and ends the session. Must not throw.

import { defineAgent, agentLog } from "@voicethere/agent";

defineAgent({
  async errorHook({ sessionId, error, customerContext }) {
    agentLog("error", `session ${sessionId}: ${error.message}`);
    // Optional: sendToClient(sessionId, { type: "alert", message: error.message });
  },
  onUserSpeechFinal({ sessionId, text }) {
    if (text.includes("crash")) throw new Error("demo failure");
  },
});

Pass opaque customerContext from the browser on session start; the client sends session_hello on voice-control open. The runner injects it as AGENT_CUSTOMER_CONTEXT JSON in the agent child env and echoes it on DC session_error payloads.

@voicethere/client — onSessionError

import { startSession, connectBrowserSession } from "@voicethere/client/browser";

const provision = await startSession({
  apiBase: sessionServiceBase,
  projectId,
  headers: { Authorization: `Bearer ${apiKey}` },
  onSessionError: (event) => {
    if (event.recoverable) showRetryToast(event.message);
    else showFatalError(event.code, event.message);
  },
});

if (provision.ok) {
  await connectBrowserSession({
    mode: "voice",
    credentials: provision.credentials,
    customerContext: { tier: "pro" },
    onSessionError: (event) => analytics.track("session_error", event),
  });
}

Configure crash TTS message

  • Dashboard: project overview → Session settings → Error TTS message. Applied on next deploy as AGENT_CRASH_TTS_MESSAGE.
  • CLI: voicethere projects session-settings set error_message "Sorry, try again."

Control plane API

List and record errors via the platform API (runner/session-service use service credentials). See Control plane API for routes and OpenAPI.

  • GET /projects/:projectId/session-errors — last 20 errors
  • GET /projects/:projectId/sessions/:sessionId/errors — errors for one session
  • Runner pods enqueue to Redis queue session-errors — session-service worker persists rows (no HTTP from runner)
  • GET/PATCH /projects/:projectId/session-settings — idle timeout + error_message

CLI

voicethere projects errors list
voicethere projects errors list --session <orchestrator-session-id>
voicethere projects session-settings list

Migration: agent_error → session_error

Older runners sent { "type": "agent_error", "message": "…" }. Current runners send the full session_error envelope above. Update client code to use onSessionError instead of parsing onControlMessage for agent_error.

← All documentation