Skip to content

Commit 631e60c

Browse files
amethystaniclaude
andauthored
fix: handle agent-initiated disconnect and guard handleEndCall (#25)
* diag: instrument ElevenLabs callbacks to capture disconnect reason Add diagnostic logging to understand why the WebSocket closes immediately after the agent starts speaking: - onDisconnect now logs reason ("error"|"agent"|"user"), closeCode, closeReason, and uptime-since-connect so we can see the root cause - onError now logs the full message + context object (not just a string cast) — the context carries the raw Event/closeCode - onModeChange logs a timestamp so we can measure the speaking→dead delta precisely - connectTimeRef tracks the connect timestamp for uptime calculation No behaviour changes — diagnostics only. The actual fix will follow once we have real disconnect-reason data from a reproduction. https://claude.ai/code/session_01SGdxNUC1TVMDtW73TZbxjW * fix: prevent double endSession causing WebSocket already-closing error When the user clicks End Call / Back: 1. handleEndCall calls conversation.endSession() — socket enters CLOSING state 2. onBack() triggers setMode('select') — UserPhoneInterface unmounts 3. The unmount cleanup fired endSession() again on the already-closed socket → "WebSocket is already in CLOSING or CLOSED state" Guard the cleanup to only call endSession when status !== 'disconnected'. https://claude.ai/code/session_01SGdxNUC1TVMDtW73TZbxjW * fix: handle agent-initiated disconnect and guard handleEndCall Two related issues: 1. When the ElevenLabs agent closes the call (reason: "agent", e.g. says "thank you for calling" then hangs up), the WebSocket is already CLOSED. If the user then clicks End Call, conversation.endSession() threw "WebSocket is already in CLOSING or CLOSED state". Fix: guard handleEndCall to skip endSession() if status === 'disconnected'. 2. When the agent closes the call, onDisconnect fired but the call session was never cleaned up — the UI stayed in the "active call" state with no way to restart. Fix: call endCall() in onDisconnect for reason !== 'user'. https://claude.ai/code/session_01SGdxNUC1TVMDtW73TZbxjW --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent c438a42 commit 631e60c

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

src/components/DemoCall/UserPhoneInterface.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ export default function UserPhoneInterface({
7676
});
7777

7878
setAgentStatus('idle');
79-
}, []);
79+
80+
// If the agent or an error closed the socket (not the user pressing End Call),
81+
// clean up the call session so the UI returns to the idle/start state.
82+
if (details?.reason !== 'user') {
83+
endCall();
84+
}
85+
}, [endCall]);
8086

8187
const onMessage = useCallback((message: { source: string; message: string }) => {
8288
console.log(`📞 [${message.source}]: ${message.message}`);
@@ -198,8 +204,13 @@ export default function UserPhoneInterface({
198204
console.log('🔴 End call button pressed');
199205
setIsEnding(true);
200206

201-
// End ElevenLabs session
202-
await conversation.endSession();
207+
// Only close the socket if it's still open — the agent may have already
208+
// closed it (e.g. it said "thank you for calling" and hung up), in which
209+
// case calling endSession() again throws "WebSocket is already in CLOSING
210+
// or CLOSED state".
211+
if (conversation.status !== 'disconnected') {
212+
await conversation.endSession();
213+
}
203214

204215
setAgentStatus('idle');
205216
await endCall();

0 commit comments

Comments
 (0)