Foundry376/Mailspring · error

ComposerEditor: ${reason}, inserting empty paragraph to reco

Error message

ComposerEditor: ${reason}, inserting empty paragraph to recover.

What it means

Self-healing warning in ComposerEditor.onChange: the incoming Slate value was structurally broken (reason is filled in by the check that fired), so a recovery operation — inserting an empty div paragraph at path [0] — is applied directly to the value before it reaches the parent, producing one clean onChange instead of a broken one plus a deferred fix.

Source

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

  };

  onChange = (change: { operations: Immutable.List<Operation>; value: Value }) => {
    // This needs to be here because some composer plugins defer their calls to onChange
    // (like spellcheck and the context menu).
    if (!this._mounted) return;

    // If the incoming value is broken, apply recovery operations directly to `value`
    // before forwarding to the parent. This emits a single onChange with the already-
    // corrected value rather than emitting the broken value and then a second onChange
    // from a deferred microtask (which is what would happen if we called editor commands
    // here). The parent therefore never sees the broken state, and any recovery operation
    // that changes the document is included in the operations list so the parent knows
    // the document changed and doesn't skip saving.
    let { operations, value } = change;

    const reason = getDocumentBrokenReason(value);
    if (reason) {
      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(

View on GitHub (pinned to 648c685d60)

Solutions

  1. No manual fix needed — the editor repairs the document automatically
  2. If it recurs, identify the plugin/plugin-paste producing invalid Slate nodes and fix its serializer
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at app/src/components/composer-editor/composer-editor.tsx:349 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/3ec823985cb016de. Report an issue: GitHub.