{"record":{"id":"f717cccb5fe5754a","repo":"pinojs/pino","slug":"only-one-of-option-transport-or-stream-can-be-spec","errorCode":null,"errorMessage":"only one of option.transport or stream can be specified","messagePattern":"only one of option\\.transport or stream can be specified","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/tools.js","lineNumber":331,"sourceCode":"      stream.end()\n    })\n  } else {\n    // For some reason istanbul is not detecting this, but it's there\n    /* istanbul ignore next */\n    // We do not have an event loop, so flush synchronously\n    stream.flushSync()\n  }\n}\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 })","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/pinojs/pino/blob/5aa62305c57709ebd6f6b7c328e8830eeb29b297/lib/tools.js#L313-L349","documentation":"Pino throws this when both a transport option and a stream argument are passed to pino(). A transport spawns a worker thread that builds its own destination stream, so a user-supplied stream would be contradictory. Only one output mechanism may be specified.","triggerScenarios":"Calling pino({ transport: { target: 'pino/file' } }, someStream) — i.e. opts.transport is truthy while the second argument is a string path or stream.","commonSituations":"Copying examples that use transport into code that already passes a destination string/file stream; migrating from pino(destination) to the newer transport API without removing the second argument; a wrapper function merging both config styles.","solutions":["Remove the second stream argument and configure the destination inside the transport options (e.g. target 'pino/file' with its options.destination).","Or drop opts.transport and keep passing the stream/destination string directly: pino('/path/to/log').","If using targets, put everything in transport.targets and pass no stream.","If the stream is required, wrap it with pino.destination and pass it as the sole stream argument instead of a transport."],"exampleFix":"// before\nconst logger = pino({ transport: { target: 'pino/file', options: { destination: './app.log' } } }, './app.log')\n// after\nconst logger = pino({ transport: { target: 'pino/file', options: { destination: './app.log' } } })","handlingStrategy":"validation","validationCode":"function validatePinoArgs(opts, stream) {\n  if (opts && opts.transport && stream != null) {\n    throw new TypeError('Pass either opts.transport OR a stream, not both')\n  }\n  return true\n}","typeGuard":"const isStreamLike = (s) => s && (typeof s === 'object') && (typeof s.writable === 'boolean' || '_writableState' in s)","tryCatchPattern":"try {\n  const logger = pino(opts, stream)\n} catch (err) {\n  if (err.message.includes('only one of option.transport or stream')) {\n    loggerFallback = pino(opts.transport ? opts : stream)\n  } else throw err\n}","preventionTips":["Pick one output mechanism per logger: transport OR stream, never both","In wrapper functions, assert the merged options don't contain both before calling pino","Prefer the transport API for new code and configure destinations inside the target options"],"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"}