denoland/deno · error · NodeRangeError
ERR_INVALID_FD
ERR_INVALID_FD
Error message
"fd" must be a positive integer: ${fd} What it means
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.
Source
Thrown at ext/node/polyfills/internal/tty.js:303
}
function removeSigwinchListener(stream) {
SetPrototypeDelete(sigwinchStreams, stream);
if (SetPrototypeGetSize(sigwinchStreams) === 0 && sigwinchRegistered) {
sigwinchRegistered = false;
Deno.removeSignalListener("SIGWINCH", onSigwinch);
}
}
// WriteStream needs to be callable without `new` to match Node.js behavior.
function WriteStream(fd) {
ensureWriteStreamPrototype();
if (!ObjectPrototypeIsPrototypeOf(WriteStream.prototype, this)) {
return new WriteStream(fd);
}
if (fd >> 0 !== fd || fd < 0) {
throw new ERR_INVALID_FD(fd);
}
// Non-stdio fds require --allow-all
op_tty_check_fd_permission(fd);
const ctx = {};
const tty = new TTY(fd, ctx);
if (ctx.code !== undefined) {
throw new ERR_TTY_INIT_FAILED(ctx);
}
FunctionPrototypeCall(lazyNet().Socket, this, {
readableHighWaterMark: 0,
handle: tty,
manualStart: true,
});
// Prevents interleaved or dropped stdout/stderr output for terminals.View on GitHub (pinned to 9ad36f7a2c)
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
Example fix
// before
const ws = new tty.WriteStream(process.env.OUT_FD); // string '1' -> throws
// after
const fd = Number(process.env.OUT_FD);
if (!Number.isInteger(fd) || fd < 0) throw new Error(`Invalid fd: ${process.env.OUT_FD}`);
const ws = new tty.WriteStream(fd); Defensive patterns
Strategy: type-guard
Validate before calling
const fd = Number(rawFd);
if (!Number.isInteger(fd) || fd < 0 || fd > 0x7fffffff) {
throw new Error(`Invalid fd: ${rawFd}`);
}
const ws = new tty.WriteStream(fd); Type guard
const isValidFd = (fd) => Number.isInteger(fd) && fd >= 0 && fd <= 0x7fffffff;
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/abb71c8bcbe77a56.
Report an issue: GitHub.