Skip to main content

Troubleshooting

Common symptoms, their usual cause, and where to read more. If something here is wrong or missing, the detailed guides (State & events, Errors & reconnects, Authentication) are the source of truth.

Audio & media

A connected call has no audio. Usually one of: you're running control-only (mediaFactory: null, so there's no media leg), the microphone was denied, or the browser blocked audio autoplay. Pass the browser mediaFactory, grant mic access, and trigger answer/dial from a user gesture (a click) so playback is allowed. See Your first softphone → Troubleshooting.

Answering raises no_media. Answering takes the ringing call's WebRTC offer and runs it through a media leg — a control-only client (no mediaFactory) has none. Supply a mediaFactory, or don't answer with audio on that client. See Errors & reconnects.

Calls & ringing

An inbound call never rings in the browser. You didn't call register() (it arms the WebRTC path), or WebRTC is off (agent.webrtcEnabled is false), so the backend bridges the call to the agent's external number instead. See Where calls ring.

An outbound callback doesn't auto-answer. A callback (a scheduled outbound call the agent accepts) arrives RINGING and waits for answerCall, like an inbound call — even with autoAnswer on. Distinguish it by CallState.source = callback.

The agent is unreachable. A reachable agent needs one of: WebRTC on (register() enables it) or an agent number set (setAgentNumber). With neither, calls can't reach them. See A device selector.

Nothing shows up in activeCalls. Either subscribe wasn't attached before the call arrived, or calls is disabled in AgentView.config for this deployment. Attach the subscriber first, and gate call UI on config.calls.enabled. See State & events.

A command seems to do nothing. The server rejects invalid commands out-of-band, on the onError callback — not as a thrown error or a state change. Wire up onError and surface it. See Command rejections.

I see a "line blocked" / can't take calls right after signing back in. This is almost always the chosen busy from your previous sign-out, not a platform fault: sign-out flips presence to busy by design (so routing stops while the token is revoked), and that presence outlives the session. Just pick an available presence again and calls will route. Reach for Reset (resetLineStatus) only for a genuine involuntary line_blocked — the server-imposed block you didn't choose. See State & events for how chosen presence differs from an involuntary block.

Connection & lifecycle

Reconnecting drops a live call's audio. close() tears down the media legs, and a reopened session won't re-answer an in-progress call, so reconnect when the agent is idle where you can. See Disconnects & reconnects.

A dropped connection isn't detected (Go). The Go client's receive loop exits silently on a stream error — there's no disconnect callback. Detect a drop from a failing intent send (Go intents return an error) or stalled updates, then reconnect. (The TypeScript client fires onError with disconnected.) See Showing a connection indicator.

The view has drifted out of sync. Every update carries a monotonic seq; if it skips, a patch was missed and the cached AgentView is stale. The SDK detects this and calls onGap — handle it by resubscribing for a fresh snapshot. See Sequence gaps.

A long-running session stops connecting. The bearer token has likely expired. Mint a fresh one (re-run your login / passwordGrant) and reconnect — don't cache a token past its lifetime. See Token lifetime.

Setup & integration

The browser blocks requests with a CORS error. If your app is served from a different origin than the babelconnect-server, that origin must be in the server's CORS allowlist. (An empty allowlist permits all origins — for development only.) See One origin, and CORS.

You can't require() the SDK in Node. @babelforce/babelconnect-sdk is ESM-only, so a CommonJS project must load it with a dynamic import: const { BabelconnectClient } = await import("@babelforce/babelconnect-sdk"). See Getting started.

config, presenceOptions, or phonebook are empty. These load on register() — before you register, AgentView.config and agent aren't populated. Call register() after subscribe. See Register.

A control-only client still receives WebRTC calls. register() marks the agent WebRTC-reachable regardless of mediaFactory. If that client takes live calls, pair it with setWebrtc(false) + setAgentNumber(...) so calls bridge to an external phone. See Control only → reachability.

The phonebook shows "recent" as a contact name. Recently-dialed numbers carry the literal label "recent". Skip those entries (label !== "recent") when resolving a name. See A contacts list.

The embedded app is blank or has no audio. Check that the host page delegates microphone to the babelconnect-server origin and that the server's CSP frame-ancestors / CORS allowlist name your host origin. See Embedding.

An embed event (e.g. cti.message) never fires. The SDK relays only what the embedded app posts, so forwarded events depend on the deployed app version. agent.loaded / user.loaded / cti.call are the reliable ones; confirm the rest against your deployment. See Embedding → events.

See also

Still stuck? The SDK sources live on GitHub — check the repo for your SDK (TypeScript, Go, or the proto contract) for open issues and the latest releases.