denoland/deno · error · TypeError

ERR_CONSOLE_WRITABLE_STREAM

ERR_CONSOLE_WRITABLE_STREAM

Error message

Console expects a writable stream instance for stdout

What it means

The Node-compatible Console class (require('node:console').Console, the options-object constructor) builds a console around streams you supply. options.stdout must be a writable stream - an object with a .write function. ERR_CONSOLE_WRITABLE_STREAM is thrown at construction when stdout is missing or lacks .write. Console accepts only Node-style streams, never file paths or WHATWG web streams.

Source

Thrown at ext/node/polyfills/internal/console/constructor.mjs:156

  if (!options || typeof options.write === "function") {
    options = {
      stdout: options,
      stderr: arguments[1],
      ignoreErrors: arguments[2],
    };
  }

  const {
    stdout,
    stderr = stdout,
    ignoreErrors = true,
    colorMode = "auto",
    inspectOptions,
    groupIndentation,
  } = options;

  if (!stdout || typeof stdout.write !== "function") {
    throw new ERR_CONSOLE_WRITABLE_STREAM("stdout");
  }
  if (!stderr || typeof stderr.write !== "function") {
    throw new ERR_CONSOLE_WRITABLE_STREAM("stderr");
  }

  if (typeof colorMode !== "boolean" && colorMode !== "auto") {
    // Match Node: reason lists the accepted values (see
    // lib/internal/console/constructor.js).
    throw new ERR_INVALID_ARG_VALUE(
      "colorMode",
      colorMode,
      "must be one of: 'auto', true, false",
    );
  }

  if (groupIndentation !== undefined) {
    validateInteger(
      groupIndentation,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass a Node-style writable: new Console({ stdout: process.stdout }) or fs.createWriteStream(path)
  2. For file logging: const out = fs.createWriteStream('./app.log', { flags: 'a' }); new Console({ stdout: out, stderr: out })
  3. Adapt web streams with a thin wrapper: { write(chunk) { writer.write(chunk); } } forwarding to a WritableStreamDefaultWriter
  4. Validate config before constructing: if (typeof opts.stdout?.write !== 'function') throw a descriptive configuration error

Example fix

// before
const { Console } = require('node:console');
const logger = new Console({ stdout: '/tmp/app.log' }); // ERR_CONSOLE_WRITABLE_STREAM

// after
const fs = require('node:fs');
const out = fs.createWriteStream('/tmp/app.log', { flags: 'a' });
const logger = new Console({ stdout: out, stderr: out });
Defensive patterns

Strategy: validation

Validate before calling

function makeConsole(opts) {
  const { stdout, stderr } = opts ?? {};
  if (typeof stdout?.write !== 'function') {
    throw new TypeError('Console options.stdout must be a Node writable stream');
  }
  if (stderr !== undefined && typeof stderr?.write !== 'function') {
    throw new TypeError('Console options.stderr must be a Node writable stream');
  }
  return new Console(opts);
}

Type guard

const isNodeWritable = (v) => v != null && typeof v.write === 'function';

Try / catch

try {
  logger = new Console(opts);
} catch (e) {
  if (e?.code === 'ERR_CONSOLE_WRITABLE_STREAM') logger = new Console({ stdout: process.stdout });
  else throw e;
}

Prevention

When it happens

Trigger: new Console({}) with no stdout; new Console({ stdout: {} }) where stdout has no write method; new Console({ stdout: '/tmp/app.log' }) passing a path string; passing a Deno.WritableStream / WritableStream (web stream) which exposes no write function.

Common situations: Building file loggers with node:console and assuming a path option exists; porting Deno-first code that passes web streams into Node APIs; forgetting destructuring defaults when options come from config objects.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/1f70b697b2cb44e4. Report an issue: GitHub.