winstonjs/winston · error · Error

Transports must WritableStreams in objectMode. Set { objectM

Error message

Transports must WritableStreams in objectMode. Set { objectMode: true }.

What it means

In `logger.add(transport)`, winston normalizes the argument (wrapping legacy `log` functions in a LegacyTransportStream) and then requires the resulting target to be a Node WritableStream created with `{ objectMode: true }`. Transport entries carry structured `info` objects, so non-objectMode streams corrupt or choke on them; the check throws to enforce this contract.

Source

Thrown at lib/winston/logger.js:377

  /**
   * Adds the transport to this logger instance by piping to it.
   * @param {mixed} transport - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   */
  add(transport) {
    // Support backwards compatibility with all existing `winston < 3.x.x`
    // transports which meet one of two criteria:
    // 1. They inherit from winston.Transport in  < 3.x.x which is NOT a stream.
    // 2. They expose a log method which has a length greater than 2 (i.e. more then
    //    just `log(info, callback)`.
    const target =
      !isStream(transport) || transport.log.length > 2
        ? new LegacyTransportStream({ transport })
        : transport;

    if (!target._writableState || !target._writableState.objectMode) {
      throw new Error(
        'Transports must WritableStreams in objectMode. Set { objectMode: true }.'
      );
    }

    // Listen for the `error` event and the `warn` event on the new Transport.
    this._onEvent('error', target);
    this._onEvent('warn', target);
    this.pipe(target);

    if (transport.handleExceptions) {
      this.exceptions.handle();
    }

    if (transport.handleRejections) {
      this.rejections.handle();
    }

    return this;

View on GitHub (pinned to ff0b79de85)

Solutions

  1. Use built-in transports (`winston.transports.Console/File/Http`) instead of hand-rolled streams.
  2. For custom transports, extend `winston-transport` and call `super({ objectMode: true, ...opts })` in the constructor.
  3. If passing a raw stream, create it with `new stream.Writable({ objectMode: true })` and implement `log(info, callback)` / `_write` accordingly.
  4. Wrap legacy log functions (`function log(level, msg, meta)`) rather than passing them alongside incompatible objects — winston wraps them in LegacyTransportStream automatically.

Example fix

// before
class MyTransport extends Transform {
  constructor(opts) { super(opts); } // not objectMode
}
logger.add(new MyTransport());

// after
class MyTransport extends Transform {
  constructor(opts) { super({ objectMode: true, ...opts }); }
  _transform(info, enc, cb) { cb(null, info); }
}
logger.add(new MyTransport());
Defensive patterns

Strategy: type-guard

Validate before calling

function canBeTransport(t) {
  return !!t && (typeof t.log === 'function') || (t && t._writableState && t._writableState.objectMode);
}
if (!canBeTransport(transport)) throw new TypeError('Transport must be an objectMode WritableStream');

Type guard

function isObjectModeWritable(x) { return !!x && !!x._writableState && x._writableState.objectMode === true; }

Try / catch

try {
  logger.add(transport);
} catch (err) {
  if (err.message.includes('objectMode')) {
    logger.add(new winston.transports.Console());
  } else { throw err; }
}

Prevention

When it happens

Trigger: Passing to `logger.add()` something that is not an objectMode writable — a plain Node `stream.Writable` without objectMode, a non-stream object that isn't a legacy log function, or a custom transport class whose constructor doesn't call `super({ objectMode: true })`.

Common situations: Writing a custom Transport subclass but forgetting `super({ objectMode: true })` in the constructor; piping a raw fs/http stream in as a transport; passing a bogus value (string/undefined) to `logger.add`.

Related errors


AI-assisted analysis of winstonjs/winston@ff0b79de85 (2026-08-31). Data as JSON: /api/errors/b870c0d177a60408. Report an issue: GitHub.