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] = protoView on GitHub (pinned to 5aa62305c5)
Solutions
- Provide a send function: transmit: { level: 'error', send: (level, logEvent) => { ... } }
- Remove the transmit option entirely if you don't need log shipping
- 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
- Always define send together with transmit: { transmit: { level, send } }
- Note that pino does not perform HTTP itself — send must implement the shipping (e.g. fetch/XHR)
- Type the browser options so transmit.send is required when transmit exists
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
- pino – redact must contain an array of strings
- unknown level ${level}
- missing bindings for child Pino
- unknown level value${level}
- 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/b8bcb19ba12c4a17.
Report an issue: GitHub.