ssssssss-team/spider-flow · error · Error

Overlays may not be stateful.

Error message

Overlays may not be stateful.

What it means

Overlays re-tokenize the visible document on every render and must be stateless — a mode with startState() needs persistent state that overlays don't support. addOverlay throws if the resolved mode defines startState.

Solutions

  1. Write a stateless overlay: a {token: function(stream){...}} spec without startState/copyState
  2. If you need a stateful mode's highlighting, set it as the editor's main mode instead of an overlay
  3. Strip startState from your overlay spec (track any needed flags with closures over plain variables only if they reset per token — state cannot persist)

Example fix

// before
cm.addOverlay({name: "javascript"}); // stateful
// after
cm.addOverlay({
  token: function(stream) {
    if (stream.match("TODO")) { return "keyword"; }
    while (stream.next() != null) {}
    return null;
  }
});
Defensive patterns

Strategy: validation

Validate before calling

function canAddOverlay(cm, spec) {
  var mode = spec.token ? spec : CodeMirror.getMode(cm.options, spec);
  return !(mode && typeof mode.startState === "function");
}
if (!canAddOverlay(cm, spec)) throw new Error("Overlay spec must be stateless");

Type guard

function isStatelessMode(mode) {
  return mode && typeof mode.token === "function" && typeof mode.startState !== "function";
}

Try / catch

try {
  cm.addOverlay(spec);
} catch (e) {
  if (/stateful/.test(e.message)) {
    cm.setOption("mode", spec); // use as main mode instead
  } else { throw e; }
}

Prevention

When it happens

Trigger: cm.addOverlay({token: fn, startState: fn}) or addOverlay('someMode') / addOverlay({name: 'javascript'}) where the underlying mode is stateful (most C-like modes are).

Common situations: Trying to overlay a full language mode (e.g. 'xml', 'javascript') instead of a small stateless highlighter, or a custom overlay spec that copied a template including startState.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of ssssssss-team/spider-flow@c799cca99c (2026-09-08). Data as JSON: /api/errors/8ee3d48804277ba6. Report an issue: GitHub.

Appendix: source

Thrown at spider-flow-web/src/main/resources/static/js/codemirror/codemirror.js:8250

      getOption: function(option) {return this.options[option]},
      getDoc: function() {return this.doc},

      addKeyMap: function(map$$1, bottom) {
        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
      },
      removeKeyMap: function(map$$1) {
        var maps = this.state.keyMaps;
        for (var i = 0; i < maps.length; ++i)
          { if (maps[i] == map$$1 || maps[i].name == map$$1) {
            maps.splice(i, 1);
            return true
          } }
      },

      addOverlay: methodOp(function(spec, options) {
        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
        insertSorted(this.state.overlays,
                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
                      priority: (options && options.priority) || 0},
                     function (overlay) { return overlay.priority; });
        this.state.modeGen++;
        regChange(this);
      }),
      removeOverlay: methodOp(function(spec) {
        var this$1 = this;

        var overlays = this.state.overlays;
        for (var i = 0; i < overlays.length; ++i) {
          var cur = overlays[i].modeSpec;
          if (cur == spec || typeof spec == "string" && cur.name == spec) {
            overlays.splice(i, 1);
            this$1.state.modeGen++;
            regChange(this$1);
            return

View on GitHub (pinned to c799cca99c)