VoiceThere

Agent crash policy

When your sandboxed agent process throws in a handler or exits unexpectedly, the runner applies the project setting agent_crash_policy. This page explains both policies, how they interact with shared_child_per_session, and what client messages can (and cannot) survive a restart_child cycle.

Where to configure

  • Dashboard: Project overview → Runner settings → Agent crash policy.
  • CLI: voicethere projects settings set agent_crash_policy <value>
  • API: PATCH /projects/:projectId/runner-settings with { "key": "agent_crash_policy", "value": "…" }— see Control plane API.

Changes are stored on the project immediately but apply to live traffic only after you Deploy to cloud (runner env is baked at deploy time as RUNNER_AGENT_CRASH_POLICY).

Values

ValueDefaultSummary
disconnect_allYesTear down affected WebRTC sessions on the runner after notifying clients with a non-recoverable session error.
restart_childNoKill and respawn the sandboxed agent child, re-register live sessions, keep WebRTC / data channels up, and notify clients with a recoverable session error.

disconnect_all

Use this when a crashed agent should end the call cleanly rather than continue with a fresh process. Typical for strict single-tenant voice pods or when you prefer the client to reconnect explicitly.

  1. Runner detects agent_error IPC (handler throw) or an unexpected child exit.
  2. Clients receive a session_error with a non-recoverable code such as AGENT_HANDLER_FAILED or AGENT_CHILD_CRASHED (see Session errors).
  3. Optional crash TTS (project session setting error_message) may play on voice sessions.
  4. Sessions on that crash scope are disconnected / torn down.

Scope with dedicated children: when shared_child_per_session=false (Advanced+), each session has its own agent child. Crashing session A does nottear down session B on the same pod — only A's child and session are affected.

Scope with a shared child: when shared_child_per_session=true, one child serves every session on the pod. A crash under disconnect_all ends all sessions on that pod that share the child.

restart_child

Use this when you want the media path to stay up while the agent process is replaced — for example multiplayer or long-lived data sessions where reconnecting WebRTC would be worse than a brief agent restart.

  1. Runner sets an internal restart window and kills the sandboxed child.
  2. A new child is spawned and each still-live session is re-registered.
  3. Clients receive AGENT_CHILD_CRASHED_RESTARTED with recoverable: true. WebRTC and data channels stay connected.
  4. The new child runs onSessionStart again (your agent may greet / re-init state).
  5. Buffered client→agent IPC is flushed after the child sends session_start_ack.

With shared_child_per_session=false, only the crashed session's child restarts; peer sessions on the same pod keep their own children and continue uninterrupted.

Message buffering during restart

While the child is restarting, the runner buffers parent→child user IPC so work is not blindly dropped:

  • data_channel_message / data_channel_binary
  • speech_event (voice turns)
  • idle_timeout

Those messages are held until the new child acknowledges session start, then delivered in order.

What is salvaged

  • Messages that arrive on the runner during the kill→spawn→ack window (restart gate).
  • Messages still waiting on the runner's outbound serial queue that have not finished child.send yet — they are moved into the restart buffer instead of being cleared.

Residual data loss (important)

The runner cannot unsend bytes already handed to a dying child over Node IPC. If the client sends a crash trigger and another message in the same instant, the follow-up may already have been child.send'd to the old process before the runner learns about the crash. Those already-sent payloads are not recovered.

In practice:

  • Queued on the runner, not yet sent → salvaged and replayed after restart (minimal loss).
  • Already sent to the dying child → may be lost; design agents and clients to tolerate a rare gap or wait for AGENT_CHILD_CRASHED_RESTARTED / a fresh agent ready signal before critical follow-ups.
  • Detection is an IPC round-trip (or process exit), not the next JavaScript tick after child.send.

Client expectations

  • Handle onSessionError for both policies (catalog).
  • On restart_child, treat AGENT_CHILD_CRASHED_RESTARTED as recoverable — keep the session; the agent may speak again after onSessionStart.
  • Do not assume every in-flight data-channel or speech turn across the crash boundary is delivered; prefer idempotent messages or a short pause after the restart notify for critical commands.

CLI examples

# List effective runner settings
voicethere projects settings list

# Prefer keep-alive restart (redeploy after set)
voicethere projects settings set agent_crash_policy restart_child
voicethere deploy --wait

# Fail closed: disconnect on crash
voicethere projects settings set agent_crash_policy disconnect_all
voicethere deploy --wait

Related

← All documentation