garrytan/gstack · error · Error

socks-bridge: unexpected listener address

Error message

socks-bridge: unexpected listener address

What it means

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`.

Source

Thrown at browse/src/socks-bridge.ts:231

        return;
      }
    };

    clientSocket.on('data', onData);
    clientSocket.on('error', () => killBoth('client error'));
  });

  await new Promise<void>((resolve, reject) => {
    const onErr = (e: unknown) => { server.off('listening', onListen); reject(e); };
    const onListen = () => { server.off('error', onErr); resolve(); };
    server.once('error', onErr);
    server.once('listening', onListen);
    server.listen(requestedPort, '127.0.0.1');
  });

  const address = server.address();
  if (!address || typeof address === 'string') {
    throw new Error('socks-bridge: unexpected listener address');
  }

  return {
    port: address.port,
    server,
    close: async () => {
      for (const sock of inFlight) {
        try { sock.destroy(); } catch { /* already gone */ }
      }
      inFlight.clear();
      await new Promise<void>((resolve) => server.close(() => resolve()));
    },
  };
}

export interface UpstreamTestOpts {
  upstream: UpstreamConfig;
  /** Hostname to test connectivity to through the upstream. Default 1.1.1.1. */

View on GitHub (pinned to 94993f7401)

Solutions

  1. Do not call `close()` on the returned bridge object before the `startSocksBridge()` promise resolves.
  2. 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.
  3. 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.
  4. Update to the latest browse version; this guard exists to fail fast rather than return a port of `undefined`.
Defensive patterns

Strategy: try-catch

Type guard

import type { AddressInfo } from 'net';
function isAddressInfo(a: any): a is AddressInfo {
  return !!a && typeof a === 'object' && typeof a.port === 'number';
}

Try / catch

try {
  const bridge = await startSocksBridge({ ... });
  return bridge;
} catch (e: any) {
  if (/socks-bridge: unexpected listener address/.test(e.message)) {
    // listener closed during bind — retry once with a fresh server
    return startSocksBridge({ ... });
  }
  throw e;
}

Prevention

When it happens

Trigger: 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`.

Common situations: 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.


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/e6149adc20b1f0e4. Report an issue: GitHub.