Skip to content

Troubleshooting

Start by watching the bridge logs (LOG_LEVEL=debug for the most detail) and GET /metrics. Most problems fall into one of the buckets below.

The upgrade is rejected (401 / 403 / 409 / 503)

Section titled “The upgrade is rejected (401 / 403 / 409 / 503)”
Symptom in logsCauseFix
rejected upgrade … bad signatureWORKER_SHARED_SECRET differs between StandIn and the bridgeCopy the pairing secret exactly into WORKER_SHARED_SECRET; restart.
rejected upgrade … stale or missing timestampClock skew beyond HMAC_FRESHNESS_MS, or the headers aren’t reaching the bridgeFix host clock (NTP); make sure your proxy forwards the X-StandIn-* (or legacy X-OpenClawTeamsBridge-*) headers.
rejected upgrade … replayed handshakeThe same handshake tuple was seen twiceUsually a benign retry; if persistent, check for a proxy duplicating the upgrade.
rejected upgrade … bridge shared secret is not configuredWORKER_SHARED_SECRET is unsetSet it. The bridge fails closed rather than accepting unauthenticated calls.
409 … already has a live sessionA second upgrade arrived for a callId already liveExpected on a reconnect/rollout; the original call keeps the slot.
503 … connection cap reachedMAX_CONNECTIONS (or per-IP) hitRaise the cap, or check for stuck sessions with bridge_calls_active on /metrics.

The call connects but the agent never joins

Section titled “The call connects but the agent never joins”

The bridge logs session.start and LiveKit room … joined, but no subscribed to agent audio.

  • LIVEKIT_AGENT_NAME mismatch - the name here must equal the agent_name your worker registered with. A typo means dispatch targets an agent that isn’t there.
  • The agent worker isn’t running / not registered for that name against this LiveKit project. Start it and confirm it appears in your LiveKit dashboard.
  • Wrong project - LIVEKIT_URL/LIVEKIT_API_KEY/LIVEKIT_API_SECRET point at a different project than the one your agent worker connects to. Dispatch only reaches agents on the same project.
  • bridge_room_connect_failures_total climbing on /metrics means the bridge couldn’t join or dispatch at all - check the API key/secret and that the URL is reachable.

The worker won’t start, or the first call takes minutes to answer

Section titled “The worker won’t start, or the first call takes minutes to answer”

Large models - avatar runtimes (bitHuman, Tavus), local STT/TTS, turn detectors - take real time to load, and that trips two setup problems worth calling out because the symptom looks like the wire path is broken when it isn’t:

  • The worker exits at startup with TimeoutError / “error initializing process”. The model load overran the process-init deadline. Raise it: WorkerOptions(..., initialize_process_timeout=300) in Python (Node’s WorkerOptions has the equivalent option). A bitHuman .imx model converting for the first time can take a couple of minutes.
  • The first call takes minutes to answer; later calls are instant. A cold job process loads the model on demand. Two fixes, use both: load the model in your prewarm function and stash it in proc.userdata (so the entrypoint reuses it), and keep a process warm with num_idle_processes >= 1 so a dispatch never waits on a cold load. Run python your_agent.py download-files once first to prefetch downloadable weights (silero VAD, the turn detector).

If a call connects and dispatches but the agent then sits silent for a long time before speaking, this - not the handshake or the room - is almost always the cause.

  • Caller can’t hear the agent - confirm the agent actually publishes an audio track (the bridge relays the first remote audio track). Avatar agents publish audio via the avatar participant; the bridge handles that, but a misconfigured avatar that publishes only video will be silent.
  • Agent can’t hear the caller - the bridge publishes caller audio as a 16 kHz mono track; make sure your agent subscribes to and feeds remote audio into its pipeline (the default AgentSession does).
  • Garbled audio - both sides must be 16 kHz PCM16. The bridge captures/relays at 16 kHz and rejects malformed (odd-length) PCM loudly; if you replaced the room connector, keep the sample rate at 16 kHz mono.
  • no worker message in 90000ms (dead peer?) - the worker stopped sending (network drop, half-open socket). The bridge ends the call so it doesn’t hold the room and the callId lock. Tune with WORKER_IDLE_TIMEOUT_MS if your path has long legitimate silences (rare - the worker heartbeats every 30 s).
  • agent … disconnected - the agent participant left the room. The bridge ends the call. Check your agent for crashes or an early ctx.shutdown().
  • Governor cutoff - MAX_CALL_MINUTES fired. Expected; raise or disable it (0) if unintended.

The governor’s goodbye is spoken by your agent, not the bridge. If the agent’s current turn outlasts GOODBYE_GRACE_MS, the goodbye is truncated. Have the teams.goodbye handler interrupt the current turn before speaking - see Agents and Dispatch - or raise GOODBYE_GRACE_MS.

If rooms outlive their calls in the LiveKit dashboard, confirm LIVEKIT_DELETE_ROOM_ON_END=true (the default) and that the API key has permission to delete rooms. On shutdown, the drain window (a couple of seconds) lets deleteRoom land; a deleteRoom failed warning in the logs points at a permissions or connectivity issue.