Bug Report
Environment
- SDK version: @deepgram/sdk v5.0.0
- Runtime: Bun v1.3.10 (macOS arm64)
- Feature affected: Live/streaming WebSocket connections (
listen.v1.connect())
Description
The listen.v1.connect() WebSocket connection fails silently in Bun. The returned socket has readyState: 3 (CLOSED) immediately, and the open event never fires. REST API endpoints (listen.v1.media.transcribeUrl, etc.) work correctly.
Root Cause
The issue is in core/websocket/ws.ts — the getGlobalWebSocket() function:
const getGlobalWebSocket = () => {
if (typeof WebSocket !== "undefined") {
return WebSocket; // ← Bun hits this (native WebSocket)
} else if (RUNTIME.type === "node") {
return NodeWebSocket; // ← never reached for Bun
}
return undefined;
};
What happens:
- The SDK correctly detects
RUNTIME.type === "bun" in runtime.mjs
- But
getGlobalWebSocket() checks typeof WebSocket !== "undefined" first — which is true in Bun (it has a native WebSocket global)
- Bun's native
WebSocket constructor ignores the 3rd options argument (including headers)
- The
ReconnectingWebSocket._connect() calls new WebSocket(url, protocols, { headers }) — the Authorization header is silently dropped
- Deepgram's server rejects the handshake (no auth) → socket goes to CLOSED
The ws npm package (imported as NodeWebSocket) handles headers correctly, but the fallback else if (RUNTIME.type === "node") is never reached because Bun satisfies the first condition.
Reproduction
import { DeepgramClient } from '@deepgram/sdk';
const client = new DeepgramClient({ apiKey: 'YOUR_KEY' });
const dg = await client.listen.v1.connect({
model: 'nova-3',
language: 'multi',
smart_format: 'true',
Authorization: 'Token YOUR_KEY',
});
console.log(dg.readyState); // 3 (CLOSED) — should be 0 (CONNECTING)
dg.on('open', () => console.log('open')); // Never fires
dg.on('error', (e) => console.log(e)); // Never fires
Workaround: Using the ws library directly to connect works perfectly in Bun:
import WS from 'ws';
const ws = new WS('wss://api.deepgram.com/v1/listen?model=nova-3', {
headers: { Authorization: 'Token YOUR_KEY' },
});
ws.on('open', () => console.log('connected!')); // ✅ Works
Suggested Fix
Add "bun" to the NodeWebSocket fallback in getGlobalWebSocket():
const getGlobalWebSocket = () => {
- if (typeof WebSocket !== "undefined") {
+ if (RUNTIME.type !== "bun" && typeof WebSocket !== "undefined") {
return WebSocket;
- } else if (RUNTIME.type === "node") {
+ } else if (RUNTIME.type === "node" || RUNTIME.type === "bun") {
return NodeWebSocket;
}
return undefined;
};
This ensures Bun uses the ws library (which supports custom headers) instead of its native WebSocket (which doesn't).
Related
Bug Report
Environment
listen.v1.connect())Description
The
listen.v1.connect()WebSocket connection fails silently in Bun. The returned socket hasreadyState: 3(CLOSED) immediately, and theopenevent never fires. REST API endpoints (listen.v1.media.transcribeUrl, etc.) work correctly.Root Cause
The issue is in
core/websocket/ws.ts— thegetGlobalWebSocket()function:What happens:
RUNTIME.type === "bun"inruntime.mjsgetGlobalWebSocket()checkstypeof WebSocket !== "undefined"first — which istruein Bun (it has a nativeWebSocketglobal)WebSocketconstructor ignores the 3rdoptionsargument (includingheaders)ReconnectingWebSocket._connect()callsnew WebSocket(url, protocols, { headers })— theAuthorizationheader is silently droppedThe
wsnpm package (imported asNodeWebSocket) handles headers correctly, but the fallbackelse if (RUNTIME.type === "node")is never reached because Bun satisfies the first condition.Reproduction
Workaround: Using the
wslibrary directly to connect works perfectly in Bun:Suggested Fix
Add
"bun"to theNodeWebSocketfallback ingetGlobalWebSocket():const getGlobalWebSocket = () => { - if (typeof WebSocket !== "undefined") { + if (RUNTIME.type !== "bun" && typeof WebSocket !== "undefined") { return WebSocket; - } else if (RUNTIME.type === "node") { + } else if (RUNTIME.type === "node" || RUNTIME.type === "bun") { return NodeWebSocket; } return undefined; };This ensures Bun uses the
wslibrary (which supports custom headers) instead of its nativeWebSocket(which doesn't).Related