denoland/deno · error · ERR_ILLEGAL_CONSTRUCTOR
ERR_ILLEGAL_CONSTRUCTOR
ERR_ILLEGAL_CONSTRUCTOR
Error message
Illegal constructor
What it means
The stream compat layer exposes stream-returning operators (pipeline/compose-style helpers) as methods installed on `Stream.Readable.prototype`. Each generated wrapper checks `new.target` and throws ERR_ILLEGAL_CONSTRUCTOR when invoked with `new`, because these are plain factory functions, not classes — their whole job is to call the operator and wrap its result in `Readable.from` or similar.
Source
Thrown at ext/node/polyfills/stream.ts:93
);
const { ERR_ILLEGAL_CONSTRUCTOR } = core.loadExtScript(
"ext:deno_node/internal/errors.ts",
);
Stream.isDestroyed = utils.isDestroyed;
Stream.isDisturbed = utils.isDisturbed;
Stream.isErrored = utils.isErrored;
Stream.isReadable = utils.isReadable;
Stream.isWritable = utils.isWritable;
Stream.Readable = Readable;
const streamKeys = ObjectKeys(streamReturningOperators);
for (let i = 0; i < streamKeys.length; i++) {
const key = streamKeys[i];
const op = streamReturningOperators[key];
function fn(...args) {
if (new.target) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
return Stream.Readable.from(ReflectApply(op, this, args));
}
ObjectDefineProperty(fn, "name", { __proto__: null, value: op.name });
ObjectDefineProperty(fn, "length", { __proto__: null, value: op.length });
ObjectDefineProperty(Stream.Readable.prototype, key, {
__proto__: null,
value: fn,
enumerable: false,
configurable: true,
writable: true,
});
}
const promiseKeys = ObjectKeys(promiseReturningOperators);
for (let i = 0; i < promiseKeys.length; i++) {
const key = promiseKeys[i];
const op = promiseReturningOperators[key];
function fn(...args) {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Call the operator without `new`: `const merged = a.compose(b);`
- Wrap operators in functions, not classes, when building utilities
- Fix typings: these are `(...args: any[]) => Readable` signatures with no construct signature
Example fix
// before const merged = new a.compose(b); // ERR_ILLEGAL_CONSTRUCTOR // after const merged = a.compose(b);
Defensive patterns
Strategy: validation
Prevention
- Treat stream operator methods (compose, pipeline-style helpers on Readable.prototype) as plain functions
- Never use `new` with prototype-installed helpers — only Stream.Readable itself is constructable
- Keep typings function-shaped so TypeScript rejects `new` at compile time
When it happens
Trigger: `new readable.compose(other)` or any `new`-invocation of an operator method from the streamReturningOperators set installed on Readable.prototype.
Common situations: Code generators or bundlers that wrap functions in classes; developers assuming every exported stream helper is a class like `new Stream.Readable()`; TypeScript typings that mistakenly declare these methods constructable.
Related errors
- ERR_HTTP_CONTENT_LENGTH_MISMATCH
- ERR_METHOD_NOT_IMPLEMENTED
- ERR_STREAM_NULL_VALUES
- ERR_HTTP_BODY_NOT_ALLOWED
- ERR_CONSTRUCT_CALL_REQUIRED
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/4723562d3cddfe86.
Report an issue: GitHub.