pinojs/pino · error · Error
pre-existing level values cannot be used for new levels
Error message
pre-existing level values cannot be used for new levels
What it means
The second collision check in assertNoLevelCollisions: it throws when a customLevels key's numeric VALUE already belongs to an existing level label in the parent. Two different names sharing one numeric value would make level filtering ambiguous, so pino rejects it.
Source
Thrown at lib/levels.js:205
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
}
if (typeof levelComparison === 'string' && Object.values(SORTING_ORDER).includes(levelComparison)) {
returnView on GitHub (pinned to 5aa62305c5)
Solutions
- Pick a numeric value not used by any existing level (check Object.values(logger.levels.values)).
- Use values outside the default range, e.g. 25, 35, 45, or above 60, ensuring uniqueness.
- Rename/remap the custom level value in your shared config so it does not clash.
- Validate the customLevels object programmatically before calling .child().
Example fix
// before
const child = logger.child({}, { customLevels: { notice: 30 } }) // 30 == info
// after
const child = logger.child({}, { customLevels: { notice: 35 } }) Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueLevelValues(parent, customLevels = {}) {
const used = new Set(Object.values(parent.levels.values))
for (const [k, v] of Object.entries(customLevels)) {
if (used.has(v)) throw new Error(`custom level '${k}' value ${v} already in use`)
}
} Type guard
function hasUniqueLevelValues(parent, customLevels) {
const used = new Set(Object.values(parent.levels.values))
return Object.values(customLevels || {}).every(v => !used.has(v))
} Try / catch
try {
child = logger.child(bindings, { customLevels })
} catch (e) {
if (e.message === 'pre-existing level values cannot be used for new levels') {
console.error('Custom level numbers collide with existing levels:', logger.levels.values)
throw e
}
throw e
} Prevention
- Pick custom level values outside the default 10-60 range and keep them in a shared constant.
- Check Object.values(logger.levels.values) before assigning numeric values.
- Avoid auto-generated sequential numbers that overlap defaults.
- Document the numeric level registry for your team to prevent duplicates.
When it happens
Trigger: logger.child({}, { customLevels: { notice: 30 } }) where 30 is 'info' in the parent; customLevels: { verbose: 20 } on a parent where 20 is 'debug'.
Common situations: Assigning sequential numbers (10, 20, 30...) to custom levels without realizing defaults already occupy 10–60; merging level tables from two services that use overlapping numeric ranges.
Related errors
- levels cannot be overridden
- 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/72396cc0b4baae40.
Report an issue: GitHub.