From 2269e0cfcf974c0c00cec8e9ff48dbfbab98c363 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 31 Jul 2026 05:26:29 +0800 Subject: [PATCH] fix(voice-call): guard synchronous realtime close --- extensions/voice-call/src/webhook/realtime-handler.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/voice-call/src/webhook/realtime-handler.ts b/extensions/voice-call/src/webhook/realtime-handler.ts index 8585f10c5886..781419a89ad7 100644 --- a/extensions/voice-call/src/webhook/realtime-handler.ts +++ b/extensions/voice-call/src/webhook/realtime-handler.ts @@ -752,6 +752,8 @@ export class RealtimeCallHandler { typeof this.providerConfig.interruptResponseOnInputAudio === "boolean" ? this.providerConfig.interruptResponseOnInputAudio : undefined; + // Providers may close synchronously before createBridge returns; no consult can exist yet. + let nativeConsultOwner: ActiveRealtimeVoiceBridge | undefined; const session = harness.createBridge({ provider: this.realtimeProvider, cfg: this.coreConfig, @@ -935,7 +937,9 @@ export class RealtimeCallHandler { this.activeBridgesByCallId.delete(callSid); this.activeTelephonyClosersByCallId.delete(callId); this.activeTelephonyClosersByCallId.delete(callSid); - this.cancelNativeConsult(callId, session); + if (nativeConsultOwner) { + this.cancelNativeConsult(callId, nativeConsultOwner); + } this.clearUserTranscriptState(callId); harness.finishOutputAudio(reason); harness.emit({ @@ -961,6 +965,7 @@ export class RealtimeCallHandler { }); }, }); + nativeConsultOwner = session; providerHandlesInputAudioBargeIn = session.bridge.handlesInputAudioBargeIn ?? providerHandlesInputAudioBargeIn; const closeTelephony = (reason: TelephonyCloseReason) => {