{"record":{"id":"d07c690922be19eb","repo":"pinojs/pino","slug":"levelval-is-read-only","errorCode":null,"errorMessage":"levelVal is read-only","messagePattern":"levelVal is read-only","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/proto.js","lineNumber":67,"sourceCode":"  version\n} = require('./meta')\nconst redaction = require('./redaction')\n\n// note: use of class is satirical\n// https://github.com/pinojs/pino/pull/433#pullrequestreview-127703127\nconst constructor = class Pino {}\nconst prototype = {\n  constructor,\n  child,\n  bindings,\n  setBindings,\n  flush,\n  isLevelEnabled,\n  version,\n  get level () { return this[getLevelSym]() },\n  set level (lvl) { this[setLevelSym](lvl) },\n  get levelVal () { return this[levelValSym] },\n  set levelVal (n) { throw Error('levelVal is read-only') },\n  get msgPrefix () { return this[msgPrefixSym] },\n  get [Symbol.toStringTag] () { return 'Pino' },\n  [lsCacheSym]: initialLsCache,\n  [writeSym]: write,\n  [asJsonSym]: asJson,\n  [getLevelSym]: getLevel,\n  [setLevelSym]: setLevel\n}\n\nObject.setPrototypeOf(prototype, EventEmitter.prototype)\n\n// exporting and consuming the prototype object using factory pattern fixes scoping issues with getters when serializing\nmodule.exports = function () {\n  return Object.create(prototype)\n}\n\nconst resetChildingsFormatter = bindings => bindings\nfunction child (bindings, options) {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/pinojs/pino/blob/5aa62305c57709ebd6f6b7c328e8830eeb29b297/lib/proto.js#L49-L85","documentation":"The logger prototype defines levelVal only as a getter over an internal symbol; its setter deliberately throws to protect the internal numeric level used for filtering. Assigning logger.levelVal = n is never valid — use setLevel or the level property instead.","triggerScenarios":"logger.levelVal = 30; Object.assign(logger, { levelVal: 30 }); copying properties from one logger object to another including levelVal.","commonSituations":"Trying to change level 'by value' after reading numeric level APIs; migrating code that manipulated internal fields; bulk-cloning logger state.","solutions":["Set the level by name instead: logger.level = 'info' or logger.setLevel('info').","If you need numeric control, define the desired value as a custom level at construction time.","Exclude levelVal when copying/cloning logger properties.","Use logger.levelVal only for reads (e.g. comparisons), never writes."],"exampleFix":"// before\nlogger.levelVal = 30\n// after\nlogger.level = 'info'","handlingStrategy":"validation","validationCode":"if ('levelVal' in changeSet && !('level' in changeSet)) {\n  throw new Error('set logger.level by name; levelVal is read-only')\n}\nObject.assign(logger, changeSet)","typeGuard":"function isLevelMutationSafe(patch) {\n  return !('levelVal' in patch)\n}","tryCatchPattern":"try {\n  applyLoggerPatch(logger, patch)\n} catch (e) {\n  if (e.message === 'levelVal is read-only') {\n    logger.setLevel(patch.levelVal)\n  } else throw e\n}","preventionTips":["Treat levelVal as a read-only numeric view; never include it in writable patches.","Change levels only via logger.level = 'name' or setLevel().","Exclude symbol-keyed and internal fields when cloning logger objects.","Code-review any code that writes to logger properties generically (Object.assign/merge)."],"tags":["api-misuse","logging","read-only"],"backgroundTag":"read-only-property-assignment","analyzedSha":"5aa62305c57709ebd6f6b7c328e8830eeb29b297","analyzedAt":"2026-09-02T22:05:21.418Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}