{"record":{"id":"d19784c3f9033522","repo":"denoland/deno","slug":"err-invalid-fd-type","errorCode":"ERR_INVALID_FD_TYPE","errorMessage":"Unsupported fd type: ${type}","messagePattern":"Unsupported fd type: (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/dgram.ts","lineNumber":460,"sourceCode":"    }\n\n    // Open an existing fd instead of creating a new one.\n    if (isBindOptions(port) && isInt32(port.fd!) && port.fd! > 0) {\n      const fd = port.fd!;\n      const state = this[kStateSymbol];\n\n      // TODO(cmorten): here we deviate somewhat from the Node implementation which\n      // makes use of the https://nodejs.org/api/cluster.html module to run servers\n      // across a \"cluster\" of Node processes to take advantage of multi-core\n      // systems.\n      //\n      // Though Deno has has a Worker capability from which we could simulate this,\n      // for now we assert that we are _always_ on the primary process.\n\n      const type = guessHandleType(fd);\n\n      if (type !== \"UDP\") {\n        throw new ERR_INVALID_FD_TYPE(type);\n      }\n\n      const err = state.handle!.open(fd);\n\n      if (err) {\n        throw errnoException(err, \"open\");\n      }\n\n      startListening(this);\n\n      return this;\n    }\n\n    let address: string;\n\n    if (isBindOptions(port)) {\n      address = port.address || \"\";\n      port = port.port;","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/dgram.ts#L442-L478","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Only pass fds that came from a real UDP socket","For systemd socket activation, declare ListenDatagram= (not ListenStream=) so the inherited fd is UDP","Drop the fd entirely and let dgram create its own socket: createSocket('udp4').bind(port)"],"exampleFix":"// before\nconst sock = dgram.createSocket(\"udp4\");\nsock.bind({ fd: inheritedTcpFd });\n// after\nconst sock = dgram.createSocket(\"udp4\");\nsock.bind(port);","handlingStrategy":"try-catch","validationCode":"function isUdpFdCandidate(fd) {\n  // fd 0/1/2 are std streams (TTY/PIPE) and can never be UDP\n  return typeof fd === \"number\" && Number.isInteger(fd) && fd > 2 && fdKnownToBeDatagram;\n}\nsock.bind(isUdpFdCandidate(fd) ? { fd } : { port });","typeGuard":null,"tryCatchPattern":"try {\n  sock.bind({ fd });\n} catch (e) {\n  if (e.code === \"ERR_INVALID_FD_TYPE\") {\n    sock = dgram.createSocket(\"udp4\"); // fall back: let dgram create its own socket\n    sock.bind(port);\n  } else throw e;\n}","preventionTips":["Only inherit fds you opened as UDP sockets","Match systemd units: ListenDatagram= with dgram, ListenStream= with net","Log fd-type mismatches loudly — they indicate wiring bugs, not runtime flakiness"],"tags":["dgram","udp","fd","systemd","socket-activation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}