{"record":{"id":"6081be498db7b7b0","repo":"pinojs/pino","slug":"option-transport-do-not-allow-stream-please-pass","errorCode":null,"errorMessage":"option.transport do not allow stream, please pass to option directly. e.g. pino(transport)","messagePattern":"option\\.transport do not allow stream, please pass to option directly\\. e\\.g\\. pino\\(transport\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/tools.js","lineNumber":339,"sourceCode":"}\n\nfunction createArgsNormalizer (defaultOptions) {\n  return function normalizeArgs (instance, caller, opts = {}, stream) {\n    // support stream as a string\n    if (typeof opts === 'string') {\n      stream = buildSafeSonicBoom({ dest: opts })\n      opts = {}\n    } else if (typeof stream === 'string') {\n      if (opts && opts.transport) {\n        throw Error('only one of option.transport or stream can be specified')\n      }\n      stream = buildSafeSonicBoom({ dest: stream })\n    } else if (opts instanceof SonicBoom || opts.writable || opts._writableState) {\n      stream = opts\n      opts = {}\n    } else if (opts.transport) {\n      if (opts.transport instanceof SonicBoom || opts.transport.writable || opts.transport._writableState) {\n        throw Error('option.transport do not allow stream, please pass to option directly. e.g. pino(transport)')\n      }\n      if (opts.transport.targets && opts.transport.targets.length && opts.formatters && typeof opts.formatters.level === 'function') {\n        throw Error('option.transport.targets do not allow custom level formatters')\n      }\n\n      let customLevels\n      if (opts.customLevels) {\n        customLevels = opts.useOnlyCustomLevels ? opts.customLevels : Object.assign({}, opts.levels, opts.customLevels)\n      }\n      stream = transport({ caller, ...opts.transport, levels: customLevels })\n    }\n    opts = Object.assign({}, defaultOptions, opts)\n    opts.serializers = Object.assign(Object.create(null), defaultOptions.serializers, opts.serializers)\n    opts.formatters = Object.assign({}, defaultOptions.formatters, opts.formatters)\n\n    if (opts.prettyPrint) {\n      throw new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)')\n    }","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/pinojs/pino/blob/5aa62305c57709ebd6f6b7c328e8830eeb29b297/lib/tools.js#L321-L357","documentation":"This fires when opts.transport is itself a stream (a SonicBoom instance or an object with writable/_writableState). Streams must be passed directly as pino's second argument, never nested inside options.transport, which only accepts a transport descriptor (target/targets string or object) for the worker thread.","triggerScenarios":"Calling pino({ transport: pino.destination('/tmp/log') }) or pino({ transport: someWritableStream }).","commonSituations":"Mistakenly nesting an existing stream under options when refactoring from pino(stream) to an options object; misreading the transport option as 'the stream to write to'; TypeScript-less code where the intended shape wasn't enforced.","solutions":["Pass the stream as the second argument instead: pino(opts, theStream).","Or if the stream is a destination file path, use pino.destination(...) and pass it directly: pino(opts, pino.destination('/tmp/log')).","Or replace the stream with a real transport descriptor: pino({ transport: { target: 'pino/file', options: { destination: '/tmp/log' } } }).","If no options besides the stream are needed, simply call pino(theStream)."],"exampleFix":"// before\nconst logger = pino({ transport: pino.destination('./app.log') })\n// after\nconst logger = pino(pino.destination('./app.log'))","handlingStrategy":"type-guard","validationCode":"function validatePinoOpts(opts) {\n  if (opts && opts.transport && (opts.transport.writable || '_writableState' in opts.transport)) {\n    throw new TypeError('Do not nest a stream in opts.transport; pass it as pino(opts, stream)')\n  }\n  return true\n}","typeGuard":"const isWritableStream = (s) => s instanceof Object && (s.writable === true || s._writableState !== undefined)","tryCatchPattern":"try {\n  const logger = pino(opts)\n} catch (err) {\n  if (err.message.includes('option.transport do not allow stream')) {\n    logger = pino(opts, opts.transport) // hoist the stream out of opts\n  } else throw err\n}","preventionTips":["Remember transport only accepts { target } / { targets } descriptors or a string module path","Pass streams (pino.destination(...), SonicBoom, net.Socket) as the second pino argument","In TypeScript, type transport as a descriptor union so stream objects are rejected at compile time"],"tags":["pino","configuration","transport","api-misuse"],"backgroundTag":"conflicting-logger-options","analyzedSha":"5aa62305c57709ebd6f6b7c328e8830eeb29b297","analyzedAt":"2026-09-02T22:05:21.418Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}