denoland/deno · error · TypeError

ERR_INVALID_FD_TYPE

ERR_INVALID_FD_TYPE

Error message

Unsupported fd type: ${type}

What it means

When a net Socket or Server is created from a raw file descriptor (new net.Socket({ fd }) or server.listen({ fd })), _createHandle asks the OS-level guessHandleType and only supports 'PIPE' and 'TCP'. Any other fd type — TTY (terminal), FILE (regular file, /dev/null), UDP — throws ERR_INVALID_FD_TYPE with the guessed type name.

Source

Thrown at ext/node/polyfills/net.ts:436

function _isPipeName(s: unknown): s is string {
  return typeof s === "string" && _toNumber(s) === false;
}

function _createHandle(fd: number, isServer: boolean): Handle {
  validateInt32(fd, "fd", 0);

  const type = guessHandleType(fd);

  if (type === "PIPE") {
    return new Pipe(isServer ? PipeConstants.SERVER : PipeConstants.SOCKET);
  }

  if (type === "TCP") {
    return new TCP(isServer ? TCPConstants.SERVER : TCPConstants.SOCKET);
  }

  throw new ERR_INVALID_FD_TYPE(type);
}

// Returns an array [options, cb], where options is an object,
// cb is either a function or null.
// Used to normalize arguments of `Socket.prototype.connect()` and
// `Server.prototype.listen()`. Possible combinations of parameters:
// - (options[...][, cb])
// - (path[...][, cb])
// - ([port][, host][...][, cb])
// For `Socket.prototype.connect()`, the [...] part is ignored
// For `Server.prototype.listen()`, the [...] part is [, backlog]
// but will not be handled here (handled in listen())
function _normalizeArgs(args: unknown[]): NormalizedArgs {
  let arr: NormalizedArgs;

  if (args.length === 0) {
    arr = [{}, null];
    arr[normalizedArgsSymbol] = true;

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Only pass fds that are actual sockets (TCP) or pipes — e.g. from socketpair, pipe(2), or a real activated socket
  2. Guard interactive/dev runs: skip the fd path unless the fd is known to be a socket (in dev, listen on a port instead)
  3. For dgram fds use node:dgram APIs, not net

Example fix

// before
server.listen({ fd: Number(process.env.LISTEN_FDS) ? 3 : 0 }); // fd 0 is a TTY in dev

// after
const fd = Number(process.env.LISTEN_FDS) > 0 ? 3 : undefined;
if (fd !== undefined) server.listen({ fd });
else server.listen(3000);
Defensive patterns

Strategy: try-catch

Validate before calling

const isPosixSocketLike = (fd: number) => {
  try {
    const st = Deno.fstatSync(fd as any); // Deno-only probe
    return st.mode != null && (st.mode & 0o140000) === 0o140000; // S_IFSOCK
  } catch { return false; }
};

Try / catch

try {
  server.listen({ fd });
} catch (e: any) {
  if (e?.code === 'ERR_INVALID_FD_TYPE') server.listen(3000); // dev fallback: plain port
  else throw e;
}

Prevention

When it happens

Trigger: server.listen({ fd: 0 }) while stdin is a terminal (type TTY); passing the fd of a regular file or /dev/null; wrapping a UDP socket fd from dgram into net; systemd-style fd handoff where the fd is not a socket/pipe.

Common situations: Systemd socket-activation code tested interactively (fd 0 becomes a TTY instead of the activated socket); scripts that try to serve over stdout redirected to a file; cluster fd-passing patterns on unsupported handle kinds.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/e7ad5c6acf790306. Report an issue: GitHub.