denoland/deno · error · TypeError

ERR_INVALID_FD_TYPE

ERR_INVALID_FD_TYPE

Error message

Unsupported fd type: ${type}

What it means

When bind() receives options with an integer fd > 0, dgram opens that existing descriptor instead of creating a socket. It inspects the descriptor with guessHandleType(fd) and requires 'UDP'; any other kind — TCP, pipe, TTY, file — throws ERR_INVALID_FD_TYPE with the detected type embedded in the message.

Source

Thrown at ext/node/polyfills/dgram.ts:460

    }

    // Open an existing fd instead of creating a new one.
    if (isBindOptions(port) && isInt32(port.fd!) && port.fd! > 0) {
      const fd = port.fd!;
      const state = this[kStateSymbol];

      // TODO(cmorten): here we deviate somewhat from the Node implementation which
      // makes use of the https://nodejs.org/api/cluster.html module to run servers
      // across a "cluster" of Node processes to take advantage of multi-core
      // systems.
      //
      // Though Deno has has a Worker capability from which we could simulate this,
      // for now we assert that we are _always_ on the primary process.

      const type = guessHandleType(fd);

      if (type !== "UDP") {
        throw new ERR_INVALID_FD_TYPE(type);
      }

      const err = state.handle!.open(fd);

      if (err) {
        throw errnoException(err, "open");
      }

      startListening(this);

      return this;
    }

    let address: string;

    if (isBindOptions(port)) {
      address = port.address || "";
      port = port.port;

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Only pass fds that came from a real UDP socket
  2. For systemd socket activation, declare ListenDatagram= (not ListenStream=) so the inherited fd is UDP
  3. Drop the fd entirely and let dgram create its own socket: createSocket('udp4').bind(port)

Example fix

// before
const sock = dgram.createSocket("udp4");
sock.bind({ fd: inheritedTcpFd });
// after
const sock = dgram.createSocket("udp4");
sock.bind(port);
Defensive patterns

Strategy: try-catch

Validate before calling

function isUdpFdCandidate(fd) {
  // fd 0/1/2 are std streams (TTY/PIPE) and can never be UDP
  return typeof fd === "number" && Number.isInteger(fd) && fd > 2 && fdKnownToBeDatagram;
}
sock.bind(isUdpFdCandidate(fd) ? { fd } : { port });

Try / catch

try {
  sock.bind({ fd });
} catch (e) {
  if (e.code === "ERR_INVALID_FD_TYPE") {
    sock = dgram.createSocket("udp4"); // fall back: let dgram create its own socket
    sock.bind(port);
  } else throw e;
}

Prevention

When it happens

Trigger: sock.bind({ fd: 1 }) or fd: 0 (stdin/stdout are TTY/PIPE, never UDP); passing a TCP listener's fd from a net.Server; passing a file fd from fs.open; fd-passing setups where the wrong descriptor index is used.

Common situations: systemd socket activation declaring ListenStream= (TCP) while the app uses dgram; SO_REUSEPORT fd sharing across processes that mixes net and dgram; test harnesses wiring arbitrary fds into the socket.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/d19784c3f9033522. Report an issue: GitHub.