{"record":{"id":"05eb0b35af9f2b6b","repo":"immerjs/immer","slug":"immer-msg","errorCode":null,"errorMessage":"[Immer] ${msg}","messagePattern":"\\[Immer\\] (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/errors.ts","lineNumber":45,"sourceCode":"\t\t\t\t\treturn `'current' expects a draft, got: ${thing}`\n\t\t\t\t},\n\t\t\t\t\"Object.defineProperty() cannot be used on an Immer draft\",\n\t\t\t\t\"Object.setPrototypeOf() cannot be used on an Immer draft\",\n\t\t\t\t\"Immer only supports deleting array indices\",\n\t\t\t\t\"Immer only supports setting array indices and the 'length' property\",\n\t\t\t\tfunction (thing: string) {\n\t\t\t\t\treturn `'original' expects a draft, got: ${thing}`\n\t\t\t\t}\n\t\t\t\t// Note: if more errors are added, the errorOffset in Patches.ts should be increased\n\t\t\t\t// See Patches.ts for additional errors\n\t\t\t]\n\t\t: []\n\nexport function die(error: number, ...args: any[]): never {\n\tif (process.env.NODE_ENV !== \"production\") {\n\t\tconst e = errors[error]\n\t\tconst msg = isFunction(e) ? e.apply(null, args as any) : e\n\t\tthrow new Error(`[Immer] ${msg}`)\n\t}\n\tthrow new Error(\n\t\t`[Immer] minified error nr: ${error}. Full error at: https://bit.ly/3cXEKWf`\n\t)\n}\n","sourceCodeStart":27,"sourceCodeEnd":51,"githubUrl":"https://github.com/immerjs/immer/blob/d2c158f5bac7081a760bbaf501ea5c360b7856e1/src/utils/errors.ts#L27-L51","documentation":"This is the single development-mode throw site of Immer's die() function (src/utils/errors.ts:41-45). die(code, ...args) indexes the errors table, formats the entry (literal string or function), and throws new Error(\"[Immer] \" + msg). Every internal die(n) call funnels here: codes 0-15 are defined in src/utils/errors.ts:3-38, and codes 16-19 are appended by the patches plugin via errorOffset 16 (src/plugins/patches.ts:36-48). It exists only when process.env.NODE_ENV !== \"production\", giving developers the full human-readable message.","triggerScenarios":"Any misuse of an Immer API: calling produce on a non-draftable value (code 1); returning a new value while also mutating the draft (code 4); a circular reference (code 5); current()/original() on a non-draft (codes 10/15); using a feature whose plugin was not enabled, e.g. patches without enablePatches() (code 0); Object.defineProperty/setPrototypeOf on a draft (11/12); deleting a non-index object property / setting non-index array keys (13/14); or applyPatches hitting an unresolvable path, reserved prop, or unsupported op (codes 18/19/17).","commonSituations":"Local dev or test runs (NODE_ENV unset or \"development\"); a value slipping past TypeScript types, such as a class instance not marked [immerable]: true; a producer that both returns a new object and mutates the draft; passing an already-finalized, frozen, or revoked proxy; mixing Map/Set patch shapes; calling finishDraft on something createDraft did not produce.","solutions":["Read the full [Immer] message - it names the exact problem; cross-reference the code index with the errors table in src/utils/errors.ts:3-38 (and the patches block at src/plugins/patches.ts:36-48 for codes >= 16).","Fix the specific misuse the message describes (return a new value OR mutate the draft, not both; mark custom classes with [immerable]: true; pass a draft from produce/createDraft to current()/original()).","Add the matching runtime check before the call (isDraftable, isDraft, isMap/isSet) so the bad input never reaches Immer.","If the message references a missing plugin (code 0), call enableXY() once at application init before first use."],"exampleFix":"// before: produce called on a non-draftable primitive -> die(1)\nproduce(123, draft => {\n  draft.value = 1\n})\n\n// after: produce on a draftable plain object\nproduce({n: 1}, draft => {\n  draft.n = 2\n})","handlingStrategy":"validation","validationCode":"// Validate the most common preconditions before calling Immer APIs.\nimport {isDraftable, isDraft, produce} from \"immer\"\n\nfunction safeProduce<S extends unknown>(\n  base: S,\n  recipe: (draft: S) => void | S\n) {\n  if (!isDraftable(base)) {\n    throw new TypeError(`Expected a draftable base, got ${typeof base}`)\n  }\n  return produce(base, recipe)\n}\n\n// guard current()/original() callers explicitly\nfunction safeOriginal<T>(d: T): T {\n  if (!isDraft(d)) throw new TypeError(\"original() expects a draft\")\n  return (d as any)[Symbol.for(\"immer-state\")].base_ as T\n}","typeGuard":"import {isDraftable, isDraft} from \"immer\"\n\nconst isSafeImmerInput = (v: unknown): v is object =>\n  isDraftable(v)\n\nconst isImmerDraft = <T>(v: T): v is T =>\n  isDraft(v)","tryCatchPattern":"try {\n  const next = produce(state, draft => {\n    /* recipe */\n  })\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"[Immer]\")) {\n    // dev message names the exact misuse and code index\n    console.error(\"Immer error:\", err.message)\n  }\n  throw err\n}","preventionTips":["Call isDraftable() on the base before produce() to catch non-draftable inputs (plain objects, arrays, Map, Set, or [immerable] classes only).","Never both return a new value and mutate the draft in one producer - pick one path.","Call isDraft() before current()/original() to avoid codes 10/15.","Enable every plugin you use (enablePatches, enableMapSet) once at app init to avoid code 0.","Keep producers pure and synchronous; do not leak drafts into async callbacks (code 3, revoked proxy)."],"tags":["die","dev-mode","error-handling","validation","immer-api"],"backgroundTag":null,"analyzedSha":"d2c158f5bac7081a760bbaf501ea5c360b7856e1","analyzedAt":"2026-08-13T09:59:10.681Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}