pinojs/pino · error · Error
levels cannot be overridden
Error message
levels cannot be overridden
What it means
assertNoLevelCollisions runs when creating a child logger with customLevels. It throws 'levels cannot be overridden' when a customLevels key already exists as a level name in the parent's values map — pino forbids redefining an existing level name on a child.
Source
Thrown at lib/levels.js:202
}
return
}
const labels = Object.assign(
Object.create(Object.prototype, { silent: { value: Infinity } }),
useOnlyCustomLevels ? null : DEFAULT_LEVELS,
customLevels
)
if (!(defaultLevel in labels)) {
throw Error(`default level:${defaultLevel} must be included in custom levels`)
}
}
function assertNoLevelCollisions (levels, customLevels) {
const { labels, values } = levels
for (const k in customLevels) {
if (k in values) {
throw Error('levels cannot be overridden')
}
if (customLevels[k] in labels) {
throw Error('pre-existing level values cannot be used for new levels')
}
}
}
/**
* Validates whether `levelComparison` is correct
*
* @throws Error
* @param {SORTING_ORDER | Function} levelComparison - value to validate
* @returns
*/
function assertLevelComparison (levelComparison) {
if (typeof levelComparison === 'function') {
return
}View on GitHub (pinned to 5aa62305c5)
Solutions
- Rename the custom level to a non-conflicting name (e.g. 'info2' or 'notice').
- Remove the conflicting key from the child's customLevels and inherit the parent level.
- If the level must differ, create a brand-new root logger instead of a child.
- Filter config-supplied custom levels against logger.levels.values before passing them.
Example fix
// before
const child = logger.child({}, { customLevels: { info: 25 } })
// after
const child = logger.child({}, { customLevels: { notice: 25 } }) Defensive patterns
Strategy: validation
Validate before calling
function safeChildCustomLevels(parent, customLevels = {}) {
const conflicts = Object.keys(customLevels).filter(k => k in parent.levels.values)
if (conflicts.length) throw new Error(`child customLevels override existing levels: ${conflicts}`)
return customLevels
}
const child = logger.child({}, { customLevels: safeChildCustomLevels(logger, cfg.customLevels) }) Type guard
function hasNoLevelNameConflicts(parent, customLevels) {
return Object.keys(customLevels || {}).every(k => !(k in parent.levels.values))
} Try / catch
try {
child = logger.child(bindings, { customLevels })
} catch (e) {
if (e.message === 'levels cannot be overridden') {
child = logger.child(bindings) // fall back to inherited levels
} else throw e
} Prevention
- Never include standard level names (trace..fatal, silent) in child customLevels.
- Prefix custom level names with a module tag, e.g. 'audit'.
- Sanitize user/config-provided level maps before passing to .child().
- Centralize child-logger creation in a helper that checks collisions once.
When it happens
Trigger: logger.child({}, { customLevels: { info: 25 } }) — 'info' is already a built-in level; logger.child(bindings, { customLevels: { debug: 15 } }) on a parent that already has debug.
Common situations: Trying to remap a standard level's value for a submodule's child logger; merging user-supplied level config that happens to contain standard level names.
Related errors
- pre-existing level values cannot be used for new levels
- Levels comparison should be one of "ASC", "DESC" or "functio
- unknown level ${level}
- default level:${defaultLevel} must be included in custom lev
- stream object needs to implement either StreamEntry or Desti
AI-assisted analysis of pinojs/pino@5aa62305c5 (2026-09-02).
Data as JSON: /api/errors/6619d71efd8d7b7a.
Report an issue: GitHub.