pinojs/pino · error · Error

pino: transmit option must have a send function

Error message

pino: transmit option must have a send function

What it means

In the browser build, pino() checks that opts.browser.transmit.send is a function when transmit is provided. The transmit option powers client-side log shipping; without a send function the library cannot transmit anything, so it throws immediately.

Source

Thrown at browser.js:95

function shouldSerialize (serialize, serializers) {
  if (Array.isArray(serialize)) {
    const hasToFilter = serialize.filter(function (k) {
      return k !== '!stdSerializers.err'
    })
    return hasToFilter
  } else if (serialize === true) {
    return Object.keys(serializers)
  }

  return false
}

function pino (opts) {
  opts = opts || {}
  opts.browser = opts.browser || {}

  const transmit = opts.browser.transmit
  if (transmit && typeof transmit.send !== 'function') { throw Error('pino: transmit option must have a send function') }

  const proto = opts.browser.write || _console
  if (opts.browser.write) opts.browser.asObject = true
  const serializers = opts.serializers || {}
  const serialize = shouldSerialize(opts.browser.serialize, serializers)
  let stdErrSerialize = opts.browser.serialize

  if (
    Array.isArray(opts.browser.serialize) &&
    opts.browser.serialize.indexOf('!stdSerializers.err') > -1
  ) stdErrSerialize = false

  const customLevels = Object.keys(opts.customLevels || {})
  const levels = ['error', 'fatal', 'warn', 'info', 'debug', 'trace'].concat(customLevels)

  if (typeof proto === 'function') {
    levels.forEach(function (level) {
      proto[level] = proto

View on GitHub (pinned to 5aa62305c5)

Solutions

  1. Provide a send function: transmit: { level: 'error', send: (level, logEvent) => { ... } }
  2. Remove the transmit option entirely if you don't need log shipping
  3. Guard the config so transmit is only set when a send function exists

Example fix

// before
pino({ browser: { transmit: { level: 'error' } } });
// after
pino({ browser: { transmit: { level: 'error', send: (level, logEvent) => fetch('/logs', { method: 'POST', body: JSON.stringify(logEvent) }) } } });
Defensive patterns

Strategy: type-guard

Validate before calling

function assertTransmit(browser = {}) {
  if (browser.transmit && typeof browser.transmit.send !== 'function') {
    throw new Error('browser.transmit.send must be a function');
  }
}

Type guard

function hasSendFn(browser) {
  return !browser.transmit || typeof browser.transmit.send === 'function';
}

Try / catch

try { logger = pino(opts); } catch (e) { if (/transmit option must have a send function/.test(e.message)) { delete opts.browser.transmit; logger = pino(opts); } else { throw e; } }

Prevention

When it happens

Trigger: pino({ browser: { transmit: { } } }) or transmit: { send: 'url' } — transmit object present but send missing or not a function.

Common situations: Copy-pasting transmit config examples and forgetting the send callback; setting send to a URL string expecting the library to do the HTTP request itself; conditional config that leaves an empty transmit object.

Related errors


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