ssssssss-team/spider-flow · error · Error
Mode " + mode.name + " failed to advance stream.
Error message
Mode " + mode.name + " failed to advance stream.
What it means
CodeMirror calls a mode's token() function up to 10 times, expecting each call to consume at least one character (stream.pos > stream.start). A mode that repeatedly consumes nothing indicates a broken or stuck highlighting mode, so CodeMirror throws to avoid an infinite loop.
Solutions
- Fix the mode's token() so every code path either advances the stream (stream.next(), stream.match non-empty) or returns null
- Check regexes in the mode for zero-width matches and anchor/skip empty tokens
- Bump to a fixed version of the third-party mode or pin a known-good CodeMirror release
Example fix
// before
if (stream.match("//")) { stream.skipToEnd(); }
return "comment"; // returns even when nothing matched
// after
if (stream.eatSpace()) { return null; }
if (stream.match("//")) { stream.skipToEnd(); return "comment"; }
stream.next();
return null; Defensive patterns
Strategy: try-catch
Validate before calling
function modeAdvances(mode, sampleText) {
try {
var state = mode.startState ? mode.startState() : null;
var stream = new CodeMirror.StringStream(sampleText);
var before = stream.pos;
mode.token(stream, state);
return stream.pos > before || stream.eol();
} catch (e) { return false; }
}
if (!modeAdvances(mode, sample)) console.warn("mode never advances"); Type guard
function isStatelessUsableMode(mode) {
return mode && typeof mode.token === "function";
} Try / catch
try {
cm.setOption("mode", suspectMode);
} catch (e) {
if (/failed to advance stream/.test(e.message)) {
cm.setOption("mode", "null"); // fall back to plain text
} else { throw e; }
} Prevention
- Test custom modes with StringStream on edge-case inputs (empty strings, EOF, unicode)
- Never return a truthy style without advancing the stream; skip spaces with stream.eatSpace() and return null
- Avoid zero-width regex matches (e.g. /x*/) in mode tokenizers
- Pin third-party modes to versions tested against your CodeMirror version
When it happens
Trigger: Installing a custom or third-party CodeMirror mode whose token() returns a style without advancing the stream (e.g. forgetting stream.next(), or a regex that matches the empty string), or a mode buggy on a specific input token.
Common situations: Custom language modes copied from older CodeMirror versions, modes with zero-length regex matches, or modes applied via {name: ...} config to content they don't handle.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Overlays may not be stateful.
- There is no line " + (n + doc.first) + " in the document.
- This document is already in use.
- Inserting collapsed marker partially overlapping an…
- Unrecognized modifier name: " + mod
AI-assisted analysis of ssssssss-team/spider-flow@c799cca99c (2026-09-08).
Data as JSON: /api/errors/b35ac3e5958b181c.
Report an issue: GitHub.
Appendix: source
Thrown at spider-flow-web/src/main/resources/static/js/codemirror/codemirror.js:1156
readToken(mode, stream, context.state);
stream.start = stream.pos;
}
}
function callBlankLine(mode, state) {
if (mode.blankLine) { return mode.blankLine(state) }
if (!mode.innerMode) { return }
var inner = innerMode(mode, state);
if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
}
function readToken(mode, stream, state, inner) {
for (var i = 0; i < 10; i++) {
if (inner) { inner[0] = innerMode(mode, state).mode; }
var style = mode.token(stream, state);
if (stream.pos > stream.start) { return style }
}
throw new Error("Mode " + mode.name + " failed to advance stream.")
}
var Token = function(stream, type, state) {
this.start = stream.start; this.end = stream.pos;
this.string = stream.current();
this.type = type || null;
this.state = state;
};
// Utility for getTokenAt and getLineTokens
function takeToken(cm, pos, precise, asArray) {
var doc = cm.doc, mode = doc.mode, style;
pos = clipPos(doc, pos);
var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
if (asArray) { tokens = []; }
while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
stream.start = stream.pos;View on GitHub (pinned to c799cca99c)