no-floating-streams
Require Node.js file stream handles to be retained so they can be closed during cleanup.
Rule catalog ID: R007
Targeted pattern scopeโ
This rule targets Node.js file stream factories from fs and node:fs:
createReadStreamcreateWriteStreamfs.createReadStreamfs.createWriteStreamrequire("fs").createReadStreamrequire("node:fs").createWriteStream
The rule reports only immediately discarded stream factory calls. It does not report assigned, returned, manager-owned, or immediately piped streams.
What this rule reportsโ
The rule reports:
- standalone file stream creation such as
createReadStream(path); - voided file stream creation such as
void fs.createWriteStream(path); - discarded CommonJS namespace or destructured factory calls
It intentionally does not require same-function destroy() or end() calls.
Streams are often owned by a pipeline, HTTP response, higher-level resource
manager, or returned API contract.
Why this rule existsโ
Node.js streams can hold file descriptors and other underlying resources until
they close or are destroyed. A discarded file stream has no reachable handle for
destroy(), end(), error handling, or ownership transfer. Unlike an
immediately piped stream, a bare discarded stream is almost always accidental.
Incorrectโ
import { createReadStream } from "node:fs";
createReadStream("input.txt");
import * as fs from "node:fs";
void fs.createWriteStream("output.txt");
const fs = require("fs");
fs.createReadStream("input.txt");
Correctโ
import { createReadStream } from "node:fs";
const stream = createReadStream("input.txt");
stream.destroy();
import * as fs from "node:fs";
return fs.createWriteStream("output.txt");
import * as fs from "node:fs";
fs.createReadStream("input.txt").pipe(response);
registerStream(fs.createReadStream("input.txt"));
Behavior and migration notesโ
Keep the stream handle in the same lifecycle owner that will observe errors and close or destroy the stream. If another API owns the stream, make that ownership explicit by returning it, passing it to a resource manager, or piping it into the destination that owns the pipeline.
This rule is intentionally narrow. It focuses on fs file stream factories
because they are common, have concrete resource ownership implications, and can
be detected reliably without type information.
This rule does not autofix. Choosing whether to store, return, pipe, or destroy the stream changes ownership semantics and must be decided by the developer.
ESLint flat config exampleโ
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
export default [
runtimeCleanup.configs.recommended,
];
When not to use itโ
Do not enable this rule for generated code or one-off scripts where discarded file stream construction is intentional and process lifetime owns the cleanup. Prefer a narrow disable comment with a reason for those cases.