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
- Use built-in transports (`winston.transports.Console/File/Http`) instead of hand-rolled streams.
- For custom transports, extend `winston-transport` and call `super({ objectMode: true, ...opts })` in the constructor.
- If passing a raw stream, create it with `new stream.Writable({ objectMode: true })` and implement `log(info, callback)` / `_write` accordingly.
- 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
- Always call `super({ objectMode: true })` in custom transport constructors (extend winston-transport)
- Never pass raw Node streams to logger.add; use built-in transports
- Add a unit test that registers every custom transport the app ships
- Type-check transport arguments in DI/wrapper code before logger.add
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
- ExceptionStream requires a TransportStream instance.
- RejectionStream requires a TransportStream instance.
- Logger is required to handle exceptions
- Logger is required for profiling
- Logger is required to handle rejections
AI-assisted analysis of winstonjs/winston@ff0b79de85 (2026-08-31).
Data as JSON: /api/errors/b870c0d177a60408.
Report an issue: GitHub.