dianping/cat · error · Error
Mark not set
Error message
Mark not set
What it means
Thrown by the CodeMirror vim keybinding's line-address parser when an ex command uses a mark address (`'{mark}`, e.g. `:'a,'b d`) but the named mark is not set on the editor, or the mark's position can no longer be resolved via `mark.find()`. Marks are per-editor state stored in `cm.state.vim.marks`; a mark set in another editor instance, a cleared mark, or a mark whose text was deleted yields no findable position. The parser deliberately throws because it cannot translate the address into a line number.
Source
Thrown at cat-home/src/main/webapp/assets/js/editor/keybinding-vim.js:4341
return result;
},
parseLineSpec_: function(cm, inputStream) {
var numberMatch = inputStream.match(/^(\d+)/);
if (numberMatch) {
return parseInt(numberMatch[1], 10) - 1;
}
switch (inputStream.next()) {
case '.':
return cm.getCursor().line;
case '$':
return cm.lastLine();
case '\'':
var mark = cm.state.vim.marks[inputStream.next()];
if (mark && mark.find()) {
return mark.find().line;
}
throw new Error('Mark not set');
default:
inputStream.backUp(1);
return undefined;
}
},
parseCommandArgs_: function(inputStream, params, command) {
if (inputStream.eol()) {
return;
}
params.argString = inputStream.match(/.*/)[0];
var delim = command.argDelimiter || /\s+/;
var args = trim(params.argString).split(delim);
if (args.length && args[0]) {
params.args = args;
}
},
matchCommand_: function(commandName) {
for (var i = commandName.length; i > 0; i--) {View on GitHub (pinned to e815e74d4c)
Solutions
- Set the mark first in normal mode (e.g. `ma` on the target line) before issuing `:'a,...` commands.
- Use a mark-independent address instead: `.` for the cursor line (`:.,$d`) or `$` for the last line.
- If scripting, verify the mark exists and is findable before dispatching the command (check `cm.state.vim.marks[name] && cm.state.vim.marks[name].find()`).
- If the marked text was deleted, re-set the mark on a stable line and retry.
Example fix
// before
cm.openDialog(':a', (cmd) => cm.state.vim.handleEx(cm, cmd)); // ':a d' -> Error: Mark not set
// after
// in normal mode press: m a (sets mark a)
// then run: :'a,'b d
if (cm.state.vim && cm.state.vim.marks['a'] && cm.state.vim.marks['a'].find()) {
cm.state.vim.handleEx(cm, "'a,'b d");
} Defensive patterns
Strategy: validation
Validate before calling
function markIsUsable(cm, name) {
var mark = cm.state.vim && cm.state.vim.marks && cm.state.vim.marks[name];
return !!(mark && typeof mark.find === 'function' && mark.find());
}
// before dispatching a command that uses a mark address:
if (!markIsUsable(cm, 'a')) { /* set it or refuse */ CodeMirror.Vim.handleEx(cm, 'ma'); } Try / catch
try {
CodeMirror.Vim.handleEx(cm, "'a,'b d");
} catch (e) {
if (/Mark not set/.test(e.message)) { showHint('Set mark first: m a'); return; }
throw e;
} Prevention
- Always create marks (m{a}) in the same editor instance before using '{a} addresses.
- Prefer '.' (cursor) and '$' (last line) addresses for scripted ranges — they never throw.
- When automating ex commands, validate mark existence with markIsUsable() first.
- Re-set marks after bulk edits that delete lines, since a mark on deleted text is unfindable.
When it happens
Trigger: Running an ex command with a `'a` style address (e.g. `:'a,'bs/old/new/g`, `:'a d`) before ever setting mark `a` with `ma` in normal mode; using marks after the marked lines were deleted so `mark.find()` returns null; using a mark set in a different CodeMirror instance sharing the keymap; buffer operations that reset `cm.state.vim.marks`.
Common situations: Users typing vim-style range commands while exploring the vim emulation; automated test suites replaying ex commands against a fresh editor with no marks initialized; plugins that pipe `:'<,'>`-style ranges with a stale register; version upgrades of the vim keybinding where mark storage moved into `cm.state.vim.marks`.
Related errors
AI-assisted analysis of dianping/cat@e815e74d4c (2026-08-14).
Data as JSON: /api/errors/69e6a6bf520f1bfa.
Report an issue: GitHub.