{"record":{"id":"a95685ba3dbf363e","repo":"ianstormtaylor/slate","slug":"cannot-set-the-key-property-of-nodes","errorCode":null,"errorMessage":"Cannot set the \"${key}\" property of nodes!","messagePattern":"Cannot set the \"(.+?)\" property of nodes!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/slate/src/interfaces/transforms/general.ts","lineNumber":256,"sourceCode":"        })\n\n        transformSelection = true\n        break\n      }\n\n      case 'set_node': {\n        const { path, properties, newProperties } = op\n\n        if (path.length === 0) {\n          throw new Error(`Cannot set properties on the root node!`)\n        }\n\n        modifyDescendant(editor, path, node => {\n          const newNode = { ...node }\n\n          for (const key in newProperties) {\n            if (NON_SETTABLE_NODE_PROPERTIES.includes(key)) {\n              throw new Error(`Cannot set the \"${key}\" property of nodes!`)\n            }\n\n            const value = newProperties[<keyof Node>key]\n\n            // Make sure we're not setting `then` to a function, since this will\n            // cause the node to be treated as a Promise-like object, which can\n            // cause unexpected behaviour when returning the node from async\n            // functions.\n            if (key === 'then' && typeof value === 'function') {\n              throw new Error(\n                'Cannot set the \"then\" property of a node to a function'\n              )\n            }\n\n            if (value == null) {\n              delete newNode[<keyof Node>key]\n            } else {\n              newNode[<keyof Node>key] = value","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/ianstormtaylor/slate/blob/72a37c701e5da4bb13a305d416d9c070d19d3a45/packages/slate/src/interfaces/transforms/general.ts#L238-L274","documentation":"set_node refuses to set properties listed in NON_SETTABLE_NODE_PROPERTIES (id, text, children) because changing them via set_node would corrupt the document structure; those changes have dedicated operations (insert_text, split_node, etc.).","triggerScenarios":"Applying set_node whose newProperties contains 'id', 'text', or 'children' — e.g. Transforms.setNodes(editor, { text: 'hi' }) or trying to change a node's id via setNodes.","commonSituations":"Trying to update node ids during a migration with setNodes instead of direct mutation; attempting text edits through setNodes; copy-pasted code that spreads a whole node into setNodes' props.","solutions":["For text, use Editor.insertText/insertTextAtPoint or insert_text/remove_text operations, or Transforms.insertText.","For children, use insert/remove/move/split node transforms instead.","For id (or custom non-structural props incorrectly grouped), check NON_SETTABLE_NODE_PROPERTIES and mutate the node directly if it's truly a custom property not in that list."],"exampleFix":"// before\nTransforms.setNodes(editor, { text: 'new text' }, { at: path })\n\n// after\nTransforms.insertText(editor, 'new text', { at: path })\n// for ids, mutate directly:\n// Node.get(editor, path).id = newId (inside withChanges)","handlingStrategy":"validation","validationCode":"const NON_SETTABLE = ['id', 'text', 'children']\nconst safe = Object.fromEntries(\n  Object.entries(props).filter(([k]) => !NON_SETTABLE.includes(k))\n)\nTransforms.setNodes(editor, safe, { at: path })","typeGuard":"function isSettableProp(key: string): boolean {\n  return !['id', 'text', 'children'].includes(key)\n}","tryCatchPattern":null,"preventionTips":["Never spread whole nodes into setNodes props.","Use text operations for text, node transforms for children.","Whitelist property keys instead of blacklisting when copying foreign objects."],"tags":["slate","set-node","immutable-props","invariant"],"backgroundTag":"operation-validation-failed","analyzedSha":"72a37c701e5da4bb13a305d416d9c070d19d3a45","analyzedAt":"2026-08-27T23:04:51.145Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}