{"record":{"id":"8b14fffd5e623da1","repo":"Foundry376/Mailspring","slug":"composereditor-selection-has-an-unset-anchor-or-f","errorCode":null,"errorMessage":"ComposerEditor: selection has an unset anchor or focus, moving to the start of the document to recover.","messagePattern":"ComposerEditor: selection has an unset anchor or focus, moving to the start of the document to recover\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/components/composer-editor/composer-editor.tsx","lineNumber":367,"sourceCode":"      console.warn(`ComposerEditor: ${reason}, inserting empty paragraph to recover.`);\n      const op = require('slate').Operation.create({\n        type: 'insert_node',\n        path: Immutable.List([0]),\n        node: Block.create({ type: 'div', nodes: Immutable.List([Text.create('')]) }),\n      });\n      operations = operations.push(op);\n      value = op.apply(value);\n    }\n\n    // Same idea as above, but for a broken selection rather than a broken document: move\n    // the dangling anchor/focus back to the start of the document so the next keystroke\n    // doesn't crash inside Slate. This check must run on `value` (which may have just been\n    // repaired above), not the original `change.value` — removing the last text-carrying\n    // node from the document unsets the selection *and* triggers the document-broken repair\n    // in the same change, and inserting the recovery paragraph does not re-anchor a\n    // previously-unset selection point, so the selection would still be broken afterwards.\n    if (isSelectionBroken(value, change.operations)) {\n      console.warn(\n        `ComposerEditor: selection has an unset anchor or focus, moving to the start of the document to recover.`\n      );\n      const firstText = value.document.getFirstText();\n      const point = Point.create({ key: firstText.key, offset: 0 });\n      value = value.setSelection({ anchor: point, focus: point });\n    }\n\n    if (value !== change.value) {\n      this.props.onChange({ operations, value });\n      return;\n    }\n\n    this.props.onChange(change);\n  };\n\n  // Event Handlers\n  render() {\n    const { className, onBlur, onDrop, value, propsForPlugins, readOnly } = this.props;","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/Foundry376/Mailspring/blob/648c685d602ece6bb00c22534b8734de6ac644b3/app/src/components/composer-editor/composer-editor.tsx#L349-L385","documentation":"Self-healing warning in ComposerEditor.onChange: the selection's anchor or focus key was unset (dangling after node deletion by a plugin), so the selection is moved to the document start to prevent the next keystroke from crashing inside Slate.","triggerScenarios":"Thrown at app/src/components/composer-editor/composer-editor.tsx:367 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["No manual action — the editor resets the selection automatically","If frequent, find the composer plugin deleting nodes without normalizing the selection"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"648c685d602ece6bb00c22534b8734de6ac644b3","analyzedAt":"2026-09-03T02:00:24.311Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}