{"record":{"id":"e6149adc20b1f0e4","repo":"garrytan/gstack","slug":"socks-bridge-unexpected-listener-address","errorCode":null,"errorMessage":"socks-bridge: unexpected listener address","messagePattern":"socks-bridge: unexpected listener address","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/socks-bridge.ts","lineNumber":231,"sourceCode":"        return;\n      }\n    };\n\n    clientSocket.on('data', onData);\n    clientSocket.on('error', () => killBoth('client error'));\n  });\n\n  await new Promise<void>((resolve, reject) => {\n    const onErr = (e: unknown) => { server.off('listening', onListen); reject(e); };\n    const onListen = () => { server.off('error', onErr); resolve(); };\n    server.once('error', onErr);\n    server.once('listening', onListen);\n    server.listen(requestedPort, '127.0.0.1');\n  });\n\n  const address = server.address();\n  if (!address || typeof address === 'string') {\n    throw new Error('socks-bridge: unexpected listener address');\n  }\n\n  return {\n    port: address.port,\n    server,\n    close: async () => {\n      for (const sock of inFlight) {\n        try { sock.destroy(); } catch { /* already gone */ }\n      }\n      inFlight.clear();\n      await new Promise<void>((resolve) => server.close(() => resolve()));\n    },\n  };\n}\n\nexport interface UpstreamTestOpts {\n  upstream: UpstreamConfig;\n  /** Hostname to test connectivity to through the upstream. Default 1.1.1.1. */","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/socks-bridge.ts#L213-L249","documentation":"Invariant thrown by the SOCKS bridge in socks-bridge.ts immediately after `listening` fires. Node's `server.address()` must return an AddressInfo object for a TCP listener; this branch catches the unexpected cases (null after close, or a string for Unix-socket/IPC bindings) before the caller reads `address.port`.","triggerScenarios":"The listener was closed between the `listening` event and the synchronous `address()` call; the server was misconfigured to bind a Unix socket/IPC path instead of a TCP port; an internal race where `server.close()` runs in the same tick as `listening`.","commonSituations":"A custom embedder that closes the bridge inside the listen promise; passing a string option where the address family expects TCP; running under a supervisor that signals SIGTERM during bind.","solutions":["Do not call `close()` on the returned bridge object before the `startSocksBridge()` promise resolves.","Confirm you are binding a TCP socket — the bridge always calls `server.listen(port, '127.0.0.1')`, so any IPC path injection in a fork would cause this.","If you see this in production, capture a core dump at the throw site — it indicates a race that should not happen in normal flow.","Update to the latest browse version; this guard exists to fail fast rather than return a port of `undefined`."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import type { AddressInfo } from 'net';\nfunction isAddressInfo(a: any): a is AddressInfo {\n  return !!a && typeof a === 'object' && typeof a.port === 'number';\n}","tryCatchPattern":"try {\n  const bridge = await startSocksBridge({ ... });\n  return bridge;\n} catch (e: any) {\n  if (/socks-bridge: unexpected listener address/.test(e.message)) {\n    // listener closed during bind — retry once with a fresh server\n    return startSocksBridge({ ... });\n  }\n  throw e;\n}","preventionTips":["Never call bridge.close() before startSocksBridge resolves.","Treat this throw as a bug report — it should not occur in normal flow.","Pin to a known-good browse version; the guard was added to fail fast on a real race.","If you fork the bridge, keep the TCP listener — do not switch to IPC paths."],"tags":["networking","invariant","socks-proxy","tcp","internal-error"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}