{"record":{"id":"abb71c8bcbe77a56","repo":"denoland/deno","slug":"err-invalid-fd","errorCode":"ERR_INVALID_FD","errorMessage":"\"fd\" must be a positive integer: ${fd}","messagePattern":"\"fd\" must be a positive integer: (.+?)","errorType":"exception","errorClass":"NodeRangeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/tty.js","lineNumber":303,"sourceCode":"}\n\nfunction removeSigwinchListener(stream) {\n  SetPrototypeDelete(sigwinchStreams, stream);\n  if (SetPrototypeGetSize(sigwinchStreams) === 0 && sigwinchRegistered) {\n    sigwinchRegistered = false;\n    Deno.removeSignalListener(\"SIGWINCH\", onSigwinch);\n  }\n}\n\n// WriteStream needs to be callable without `new` to match Node.js behavior.\nfunction WriteStream(fd) {\n  ensureWriteStreamPrototype();\n  if (!ObjectPrototypeIsPrototypeOf(WriteStream.prototype, this)) {\n    return new WriteStream(fd);\n  }\n\n  if (fd >> 0 !== fd || fd < 0) {\n    throw new ERR_INVALID_FD(fd);\n  }\n\n  // Non-stdio fds require --allow-all\n  op_tty_check_fd_permission(fd);\n\n  const ctx = {};\n  const tty = new TTY(fd, ctx);\n  if (ctx.code !== undefined) {\n    throw new ERR_TTY_INIT_FAILED(ctx);\n  }\n\n  FunctionPrototypeCall(lazyNet().Socket, this, {\n    readableHighWaterMark: 0,\n    handle: tty,\n    manualStart: true,\n  });\n\n  // Prevents interleaved or dropped stdout/stderr output for terminals.","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/tty.js#L285-L321","documentation":"tty.WriteStream validates its fd with the 32-bit check 'fd >> 0 !== fd || fd < 0', accepting only non-negative integers representable in int32. Negative numbers, fractions, NaN, strings, and values above 2^31-1 throw ERR_INVALID_FD before any syscall is attempted.","triggerScenarios":"new tty.WriteStream(-1); new tty.WriteStream(1.5); new tty.WriteStream(process.env.OUT_FD) where the value is a string like '1'; a computed fd that turns out NaN.","commonSituations":"Reading fd numbers from env vars/CLI flags as strings; -1 sentinels from other APIs; fds handed back by native layers that use -1 for errors.","solutions":["Pass a real numeric fd: 0, 1, 2, or one returned by fs.openSync()","Coerce and check: const fd = Number(input); if (!Number.isInteger(fd) || fd < 0) throw ...","Use null plus an explicit branch instead of -1 as a 'no fd' sentinel"],"exampleFix":"// before\nconst ws = new tty.WriteStream(process.env.OUT_FD); // string '1' -> throws\n\n// after\nconst fd = Number(process.env.OUT_FD);\nif (!Number.isInteger(fd) || fd < 0) throw new Error(`Invalid fd: ${process.env.OUT_FD}`);\nconst ws = new tty.WriteStream(fd);","handlingStrategy":"type-guard","validationCode":"const fd = Number(rawFd);\nif (!Number.isInteger(fd) || fd < 0 || fd > 0x7fffffff) {\n  throw new Error(`Invalid fd: ${rawFd}`);\n}\nconst ws = new tty.WriteStream(fd);","typeGuard":"const isValidFd = (fd) => Number.isInteger(fd) && fd >= 0 && fd <= 0x7fffffff;","tryCatchPattern":null,"preventionTips":["Coerce env/CLI fd strings with Number() and validate before use","Never use -1 as a sentinel fd; use null and branch","Remember the check is 32-bit — very large fd values fail too"],"tags":["tty","file-descriptor","node-compat","deno"],"backgroundTag":"invalid-file-descriptor","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-26T04:18:47.238Z"}