{"record":{"id":"b870c0d177a60408","repo":"winstonjs/winston","slug":"transports-must-writablestreams-in-objectmode-set","errorCode":null,"errorMessage":"Transports must WritableStreams in objectMode. Set { objectMode: true }.","messagePattern":"Transports must WritableStreams in objectMode\\. Set (.+?)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/winston/logger.js","lineNumber":377,"sourceCode":"\n  /**\n   * Adds the transport to this logger instance by piping to it.\n   * @param {mixed} transport - TODO: add param description.\n   * @returns {Logger} - TODO: add return description.\n   */\n  add(transport) {\n    // Support backwards compatibility with all existing `winston < 3.x.x`\n    // transports which meet one of two criteria:\n    // 1. They inherit from winston.Transport in  < 3.x.x which is NOT a stream.\n    // 2. They expose a log method which has a length greater than 2 (i.e. more then\n    //    just `log(info, callback)`.\n    const target =\n      !isStream(transport) || transport.log.length > 2\n        ? new LegacyTransportStream({ transport })\n        : transport;\n\n    if (!target._writableState || !target._writableState.objectMode) {\n      throw new Error(\n        'Transports must WritableStreams in objectMode. Set { objectMode: true }.'\n      );\n    }\n\n    // Listen for the `error` event and the `warn` event on the new Transport.\n    this._onEvent('error', target);\n    this._onEvent('warn', target);\n    this.pipe(target);\n\n    if (transport.handleExceptions) {\n      this.exceptions.handle();\n    }\n\n    if (transport.handleRejections) {\n      this.rejections.handle();\n    }\n\n    return this;","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/winstonjs/winston/blob/ff0b79de8562bb322c390fbc82fe71c11f373428/lib/winston/logger.js#L359-L395","documentation":"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.","triggerScenarios":"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 })`.","commonSituations":"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`.","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."],"exampleFix":"// before\nclass MyTransport extends Transform {\n  constructor(opts) { super(opts); } // not objectMode\n}\nlogger.add(new MyTransport());\n\n// after\nclass MyTransport extends Transform {\n  constructor(opts) { super({ objectMode: true, ...opts }); }\n  _transform(info, enc, cb) { cb(null, info); }\n}\nlogger.add(new MyTransport());","handlingStrategy":"type-guard","validationCode":"function canBeTransport(t) {\n  return !!t && (typeof t.log === 'function') || (t && t._writableState && t._writableState.objectMode);\n}\nif (!canBeTransport(transport)) throw new TypeError('Transport must be an objectMode WritableStream');","typeGuard":"function isObjectModeWritable(x) { return !!x && !!x._writableState && x._writableState.objectMode === true; }","tryCatchPattern":"try {\n  logger.add(transport);\n} catch (err) {\n  if (err.message.includes('objectMode')) {\n    logger.add(new winston.transports.Console());\n  } else { throw err; }\n}","preventionTips":["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"],"tags":["winston","streams","objectmode","transport","argument-validation"],"backgroundTag":"invalid-transport-stream","analyzedSha":"ff0b79de8562bb322c390fbc82fe71c11f373428","analyzedAt":"2026-08-31T13:33:44.585Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}