pinojs/pino · error · Error
only one of option.transport or stream can be specified
Error message
only one of option.transport or stream can be specified
What it means
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.
Source
Thrown at lib/tools.js:331
stream.end()
})
} else {
// For some reason istanbul is not detecting this, but it's there
/* istanbul ignore next */
// We do not have an event loop, so flush synchronously
stream.flushSync()
}
}
function createArgsNormalizer (defaultOptions) {
return function normalizeArgs (instance, caller, opts = {}, stream) {
// support stream as a string
if (typeof opts === 'string') {
stream = buildSafeSonicBoom({ dest: opts })
opts = {}
} else if (typeof stream === 'string') {
if (opts && opts.transport) {
throw Error('only one of option.transport or stream can be specified')
}
stream = buildSafeSonicBoom({ dest: stream })
} else if (opts instanceof SonicBoom || opts.writable || opts._writableState) {
stream = opts
opts = {}
} else if (opts.transport) {
if (opts.transport instanceof SonicBoom || opts.transport.writable || opts.transport._writableState) {
throw Error('option.transport do not allow stream, please pass to option directly. e.g. pino(transport)')
}
if (opts.transport.targets && opts.transport.targets.length && opts.formatters && typeof opts.formatters.level === 'function') {
throw Error('option.transport.targets do not allow custom level formatters')
}
let customLevels
if (opts.customLevels) {
customLevels = opts.useOnlyCustomLevels ? opts.customLevels : Object.assign({}, opts.levels, opts.customLevels)
}
stream = transport({ caller, ...opts.transport, levels: customLevels })View on GitHub (pinned to 5aa62305c5)
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.
Example fix
// before
const logger = pino({ transport: { target: 'pino/file', options: { destination: './app.log' } } }, './app.log')
// after
const logger = pino({ transport: { target: 'pino/file', options: { destination: './app.log' } } }) Defensive patterns
Strategy: validation
Validate before calling
function validatePinoArgs(opts, stream) {
if (opts && opts.transport && stream != null) {
throw new TypeError('Pass either opts.transport OR a stream, not both')
}
return true
} Type guard
const isStreamLike = (s) => s && (typeof s === 'object') && (typeof s.writable === 'boolean' || '_writableState' in s)
Try / catch
try {
const logger = pino(opts, stream)
} catch (err) {
if (err.message.includes('only one of option.transport or stream')) {
loggerFallback = pino(opts.transport ? opts : stream)
} else throw err
} Prevention
- 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
When it happens
Trigger: Calling pino({ transport: { target: 'pino/file' } }, someStream) — i.e. opts.transport is truthy while the second argument is a string path or stream.
Common situations: 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.
Related errors
- option.transport do not allow stream, please pass to option
- only one of target or targets can be specified
- option.transport.targets do not allow custom level formatter
- missing bindings for child Pino
- Levels comparison should be one of "ASC", "DESC" or "functio
AI-assisted analysis of pinojs/pino@5aa62305c5 (2026-09-02).
Data as JSON: /api/errors/f717cccb5fe5754a.
Report an issue: GitHub.