Troubleshooting & FAQ
Troubleshooting & FAQ
Troubleshooting & FAQ
Common failure modes and how to diagnose them. For end-to-end patterns, see the guides; for full API contracts, see the reference.
InvalidCredentialsError on connectCause: Token is expired, malformed, or minted for a different SignalWire space.
Fix:
Errors that happen outside of an await flow surface on
errors$. If you
never subscribe to it, those errors are silently dropped — subscribe
during client construction so they always reach your logs.
NotConnectedError when calling dial()Cause: Calling dial() before the client finished connecting.
Wait for ready$ to emit true:
Causes: Unstable network, corporate firewall/proxy blocking WebSocket, idle timeout.
The SDK reconnects automatically. Drive a “reconnecting” banner off
isConnected$:
Causes: Camera permissions denied, camera in use by another app, wrong camera selected, hardware issue.
Causes: Output device not selected, remote participant muted, browser blocking unmuted autoplay, or the muted attribute left on the <video> element.
Browsers block autoplay with audio until the page has been interacted
with — play() will reject. Catch that promise and surface a
play-to-start button if it fails.
Use headphones, or enable echo cancellation:
Permission denied for camera/microphoneHTTPS is required. getUserMedia only works on secure contexts (HTTPS or localhost).
trying stateCauses: Invalid destination, destination not reachable, network/firewall blocking.
Cause: ICE connection failed (firewall blocking UDP) or TURN server unreachable.
Causes: register()
was never called, or the token can’t register. Only full Subscriber
Access Tokens can register; Guest SATs, Invite SATs, and embed tokens
are outbound-only.
Inbound calls then surface on session.incomingCalls$.
Send digits only after the call is connected:
Subscribe immediately after getting the object — BehaviorSubjects emit current state on subscribe.
Almost always a missing unsubscribe on a long-lived subscription. See RxJS Primer → Cleanup and the framework patterns in Framework Integration.
Safari has strict autoplay policies. Add playsinline and handle the play promise:
Firefox doesn’t fully support setSinkId. Audio plays through default output.
Device rotation or app switching can reset the camera.
The SDK emits debug-level logs to the browser console. Filter by
signalwire in DevTools to isolate them.
DevTools → Network → “WS” filter → click the connection → Messages tab.
call.rtcPeerConnection
is the underlying RTCPeerConnection — it’s undefined until media
negotiation starts, so guard before reading it.
Chrome flag: --use-fake-device-for-media-stream. Or generate a canvas stream:
Yes for production. WebRTC’s getUserMedia requires a secure context. Localhost is exempt for development.
Modern Chrome, Firefox, Safari, and Edge. No IE11.
No — the SDK is browser-only. Use the SignalWire REST APIs or server-side SDKs.
call.self is null until the local participant joins — always check.
Recording is controlled by the SignalWire platform, not the browser.
startRecording()
is on the API surface but not yet implemented in v4 and will throw
when called. Recording state configured server-side still surfaces
through recording$.
Some methods are on the API surface but pending implementation, and
others depend on capabilities the server hasn’t granted to this call.
Check capabilities$
to gate the UI on what’s actually available.