From fdb2f686f7df7a9ccb9395877e6e939488c518fa Mon Sep 17 00:00:00 2001 From: coygg Date: Fri, 3 Apr 2026 12:31:44 -0400 Subject: [PATCH 1/2] fix: prevent SIP connect hang during telnyx init --- src/hooks/use-telnyx.ts | 42 ++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/hooks/use-telnyx.ts b/src/hooks/use-telnyx.ts index c62d8307..5b8a0800 100644 --- a/src/hooks/use-telnyx.ts +++ b/src/hooks/use-telnyx.ts @@ -82,6 +82,7 @@ export function useTelnyx(): UseTelnyxReturn { const callRef = useRef(null) const durationIntervalRef = useRef | null>(null) const callStartTimeRef = useRef(null) + const connectStartTimeRef = useRef(0) const [connectionStatus, setConnectionStatus] = useState('disconnected') const [isRegistered, setIsRegistered] = useState(false) @@ -622,23 +623,29 @@ export function useTelnyx(): UseTelnyxReturn { }) clientRef.current = client - await client.connect() + connectStartTimeRef.current = Date.now() - // Safety net: if telnyx.ready doesn't fire within REGISTRATION_TIMEOUT_MS, - // reset status from 'connecting' to 'disconnected' so periodic checks - // can retry. Without this, a failed registration leaves status stuck on - // 'connecting' and all ensureRegistered() calls bail early. + // Arm timeout BEFORE awaiting connect to avoid hangs where connect() never resolves. if (registrationTimeoutRef.current) { clearTimeout(registrationTimeoutRef.current) } - registrationTimeoutRef.current = setTimeout(() => { - if (destroyed) return - if (connectionStatusRef.current === 'connecting') { - console.warn('[useTelnyx] Registration timeout - telnyx.ready not received within', REGISTRATION_TIMEOUT_MS, 'ms, resetting to disconnected') + const timeoutPromise = new Promise((_, reject) => { + registrationTimeoutRef.current = setTimeout(() => { + if (destroyed) return + + console.warn( + '[useTelnyx] SIP connect timeout - connect/ready not received within', + REGISTRATION_TIMEOUT_MS, + 'ms' + ) markInitialized() - setConnectionStatus('disconnected') - } - }, REGISTRATION_TIMEOUT_MS) + setConnectionStatus('error') + setRegistrationState(false) + reject(new Error('SIP connect timeout')) + }, REGISTRATION_TIMEOUT_MS) + }) + + await Promise.race([client.connect(), timeoutPromise]) } catch (err) { console.error('[useTelnyx] Failed to initialize:', err) if (!destroyed) setConnectionStatus('error') @@ -734,7 +741,16 @@ export function useTelnyx(): UseTelnyxReturn { async function ensureRegistered(reason: string) { if (destroyed || isInitializingRef.current) return false - if (connectionStatusRef.current === 'connecting') return false + if (connectionStatusRef.current === 'connecting') { + const elapsed = Date.now() - connectStartTimeRef.current + if (connectStartTimeRef.current > 0 && elapsed > REGISTRATION_TIMEOUT_MS) { + console.warn( + `[useTelnyx] ensureRegistered detected stuck connecting (${reason}) after ${elapsed}ms - triggering recovery` + ) + runLifecycleRecovery('connect-timeout') + } + return false + } const activeClient = clientRef.current if (!activeClient) { From 4c0c93b70e66639648e36583ecf96a9ac8e87025 Mon Sep 17 00:00:00 2001 From: coygg Date: Fri, 3 Apr 2026 18:35:48 -0400 Subject: [PATCH 2/2] fix: trigger reconnect on stuck connecting detection --- src/hooks/use-telnyx.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/use-telnyx.ts b/src/hooks/use-telnyx.ts index 5b8a0800..5c43712e 100644 --- a/src/hooks/use-telnyx.ts +++ b/src/hooks/use-telnyx.ts @@ -747,7 +747,7 @@ export function useTelnyx(): UseTelnyxReturn { console.warn( `[useTelnyx] ensureRegistered detected stuck connecting (${reason}) after ${elapsed}ms - triggering recovery` ) - runLifecycleRecovery('connect-timeout') + await triggerManualReconnect('connect-timeout') } return false }