pinojs/pino · error · Error

option.transport do not allow stream, please pass to option

Error message

option.transport do not allow stream, please pass to option directly. e.g. pino(transport)

What it means

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.

Source

Thrown at lib/tools.js:339

}

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 })
    }
    opts = Object.assign({}, defaultOptions, opts)
    opts.serializers = Object.assign(Object.create(null), defaultOptions.serializers, opts.serializers)
    opts.formatters = Object.assign({}, defaultOptions.formatters, opts.formatters)

    if (opts.prettyPrint) {
      throw new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)')
    }

View on GitHub (pinned to 5aa62305c5)

Solutions

  1. Pass the stream as the second argument instead: pino(opts, theStream).
  2. Or if the stream is a destination file path, use pino.destination(...) and pass it directly: pino(opts, pino.destination('/tmp/log')).
  3. Or replace the stream with a real transport descriptor: pino({ transport: { target: 'pino/file', options: { destination: '/tmp/log' } } }).
  4. If no options besides the stream are needed, simply call pino(theStream).

Example fix

// before
const logger = pino({ transport: pino.destination('./app.log') })
// after
const logger = pino(pino.destination('./app.log'))
Defensive patterns

Strategy: type-guard

Validate before calling

function validatePinoOpts(opts) {
  if (opts && opts.transport && (opts.transport.writable || '_writableState' in opts.transport)) {
    throw new TypeError('Do not nest a stream in opts.transport; pass it as pino(opts, stream)')
  }
  return true
}

Type guard

const isWritableStream = (s) => s instanceof Object && (s.writable === true || s._writableState !== undefined)

Try / catch

try {
  const logger = pino(opts)
} catch (err) {
  if (err.message.includes('option.transport do not allow stream')) {
    logger = pino(opts, opts.transport) // hoist the stream out of opts
  } else throw err
}

Prevention

When it happens

Trigger: Calling pino({ transport: pino.destination('/tmp/log') }) or pino({ transport: someWritableStream }).

Common situations: 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.

Related errors


AI-assisted analysis of pinojs/pino@5aa62305c5 (2026-09-02). Data as JSON: /api/errors/6081be498db7b7b0. Report an issue: GitHub.