# Troubleshooting Diagnose configuration, protocol events, execution and playback separately. Use [architecture](architecture.md), [tool calling](tool-calling.md) and [context injection](context-injection.md) to identify which path is active. ## Check configuration and connection first Inspect the server configuration without printing secret environment files: ```bash curl -fsS http://localhost:8420/api/config | jq ``` Confirm `s2sUrl`, `lb`, `reasoningType`, `injectProfile`, `apipilot`, `theme` and `startSessionMode`. A direct WebSocket URL bypasses the local relay's server-side context injection and marker execution. For a local proxy, confirm the server can resolve and reach `S2S_PROXY_TARGET`. In browser DevTools, inspect the WebSocket handshake and `session.created`, `session.update` and error events. Check that instructions and native tools are inside `session`. A cached browser module can disagree with current server code; inspect served asset versions and reload with cache disabled when needed. ## The assistant promises a tool but nothing happens Follow the actual execution mechanism: 1. For a native tool, confirm it appears in the outbound `session.tools` array. 2. Find `response.function_call_arguments.done` with a tool name, `call_id`, and JSON-string arguments. 3. Confirm the browser dispatched `toolcall` and the executor sent its request. 4. Confirm `function_call_output` and any required `response.create` were sent. 5. For ApiPilot, inspect discovery, marker detection, authorization/schema validation, execution, and second-pass result injection instead. A spoken sentence such as “Voy a buscar el reporte” does not satisfy step 2. If the function-call event is absent, inspect model/backend tool generation before debugging a browser executor that has not been invoked. Earlier local debug notes recorded a reproduction where tools and required tool policy were sent but the model produced only spoken text. Those notes refer to the retired MCP ranking path; they do not establish a current ApiPilot failure. Both local investigation notes remain marked `OPEN`. Their useful lesson is to collect evidence at each boundary and reproduce against current code before claiming a root cause is resolved. ## The assistant misses caller or report context - Verify the page has an encoded `phoneNumber` or the intended `episode_id`. - Inspect `/api/user/phone` and context-fetch logs for HTTP/JSON failures. - Confirm profile injection is enabled; a visible profile alone does not prove realtime instructions contain it. - Reports need `S2S_INJECT_REPORTS=true`; `minimal` sends only labels and dates. A historical script will not be in the prompt in that mode. - Static summary text overrides the HTTP summary endpoint. - Inspect the final `session.update` rather than only its browser base prompt. The proxy can append and deduplicate context after the browser sends it. `S2S_DEBUG_PROMPT=true` enables complete prompt logs, including caller information and conversation text. Use summaries when full payloads are unnecessary. The browser's application functions are module-scoped; do not assume names such as `effectiveInstructions()` are available directly on `window` in DevTools. ## Short gaps or clipped words around tool results The retained audio investigation found very low local serialization/render times for one large tool result, while still observing audio interruptions. That evidence ruled out a UI-render bottleneck for that reproduction; it does not rule out rendering costs for all future payloads. Useful checks are: - Measure tool-result size, prompt length, serialization time and UI render time. - Inspect whether result injection overlaps an active or pending response. - Check whether a speculative response starts before its `response.created` reaches the browser. Cancellation based only on an existing response ID can miss that window. - Inspect deferred text/response queues and whether they flush after `response.done` rather than interrupting the active turn. - Inspect playback prebuffering and queue underflow, especially when the first words of every sentence are affected. - Confirm oversized tool results are summarized or capped on the relevant path rather than assuming every tool payload gets the same treatment. The historical investigation introduced a 90ms playback prebuffer and reduced MCP prompt reinjection. The old 150ms MCP fallback timer is historical, not a current ApiPilot configuration knob. Verify the live worklet and current relay before changing audio timing based on those observations. ## External reasoning and startup `/api/reasoning` returning 409 is expected in `PROXY` mode. In `NODE_RED`, check that `S2S_REASONING_NODE_RED_URL` is reachable from the server and whether the response is `log_only=true`. An empty answer is valid only in log-only mode. In `OPENAI`, check the configured endpoint/model/key and provider logs; HTTP reasoning does not use the native session's tool array. For `TALK`/`TALKFIRST`, the two-second warmup does not override browser microphone/audio permissions. If automatic startup fails, use the unlocker gesture and inspect the permission error rather than increasing the timer without evidence. ## Read-only source checks From the repository root, useful syntax checks are: ```bash node --check s2s-client/main.js node --check s2s-client/ws/s2s-ws-client.js python -m py_compile s2s-client/server.py ``` Syntax checks do not validate microphone access, network services, model output, or audio quality. Collect a fresh browser/backend reproduction for those paths.