diff --git a/.changeset/fix-connectws-timeout-socket-leak.md b/.changeset/fix-connectws-timeout-socket-leak.md new file mode 100644 index 000000000..1e9abbdf3 --- /dev/null +++ b/.changeset/fix-connectws-timeout-socket-leak.md @@ -0,0 +1,5 @@ +--- +'@livekit/agents': patch +--- + +Fix orphaned WebSocket leak in `connectWs`: when the connection timeout fires, the socket is now terminated so it cannot connect and linger without an owner. Also fixes a hang where a normal (code 1000) close during the handshake left the promise unsettled — it now rejects on any close before the socket opens. Uses `APITimeoutError` instead of `APIConnectionError` for clearer retry semantics. diff --git a/agents/src/inference/utils.ts b/agents/src/inference/utils.ts index 8a9b8319e..334b9e3ec 100644 --- a/agents/src/inference/utils.ts +++ b/agents/src/inference/utils.ts @@ -4,7 +4,7 @@ import { ThrowsPromise } from '@livekit/throws-transformer/throws'; import { AccessToken } from 'livekit-server-sdk'; import { WebSocket } from 'ws'; -import { APIConnectionError, APIStatusError } from '../_exceptions.js'; +import { APIConnectionError, APIStatusError, APITimeoutError } from '../_exceptions.js'; import { getJobContext } from '../job.js'; import { version } from '../version.js'; @@ -90,12 +90,16 @@ export async function connectWs( return new ThrowsPromise((resolve, reject) => { const socket = new WebSocket(url, { headers: { ...buildMetadataHeaders(), ...headers } }); + let opened = false; + const timeout = setTimeout(() => { - reject(new APIConnectionError({ message: 'Timeout connecting to LiveKit WebSocket' })); + socket.terminate(); + reject(new APITimeoutError({ message: 'Timeout connecting to LiveKit WebSocket' })); }, timeoutMs); const onOpen = () => { clearTimeout(timeout); + opened = true; resolve(socket); }; @@ -113,9 +117,9 @@ export async function connectWs( } }; - const onClose = (code: number) => { + const onClose = () => { clearTimeout(timeout); - if (code !== 1000) { + if (!opened) { reject( new APIConnectionError({ message: 'Connection closed unexpectedly',