Foundry376/Mailspring · error

ComposerEditor: selection has an unset anchor or focus, movi

Error message

ComposerEditor: selection has an unset anchor or focus, moving to the start of the document to recover.

What it means

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.

Source

Thrown at app/src/components/composer-editor/composer-editor.tsx:367

      console.warn(`ComposerEditor: ${reason}, inserting empty paragraph to recover.`);
      const op = require('slate').Operation.create({
        type: 'insert_node',
        path: Immutable.List([0]),
        node: Block.create({ type: 'div', nodes: Immutable.List([Text.create('')]) }),
      });
      operations = operations.push(op);
      value = op.apply(value);
    }

    // Same idea as above, but for a broken selection rather than a broken document: move
    // the dangling anchor/focus back to the start of the document so the next keystroke
    // doesn't crash inside Slate. This check must run on `value` (which may have just been
    // repaired above), not the original `change.value` — removing the last text-carrying
    // node from the document unsets the selection *and* triggers the document-broken repair
    // in the same change, and inserting the recovery paragraph does not re-anchor a
    // previously-unset selection point, so the selection would still be broken afterwards.
    if (isSelectionBroken(value, change.operations)) {
      console.warn(
        `ComposerEditor: selection has an unset anchor or focus, moving to the start of the document to recover.`
      );
      const firstText = value.document.getFirstText();
      const point = Point.create({ key: firstText.key, offset: 0 });
      value = value.setSelection({ anchor: point, focus: point });
    }

    if (value !== change.value) {
      this.props.onChange({ operations, value });
      return;
    }

    this.props.onChange(change);
  };

  // Event Handlers
  render() {
    const { className, onBlur, onDrop, value, propsForPlugins, readOnly } = this.props;

View on GitHub (pinned to 648c685d60)

Solutions

  1. No manual action — the editor resets the selection automatically
  2. If frequent, find the composer plugin deleting nodes without normalizing the selection
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at app/src/components/composer-editor/composer-editor.tsx:367 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Foundry376/Mailspring@648c685d60 (2026-09-03). Data as JSON: /api/errors/8b14fffd5e623da1. Report an issue: GitHub.