{"record":{"id":"ccb87ed95fcd2b96","repo":"ajaxorg/ace","slug":"mark-not-set","errorCode":null,"errorMessage":"Mark not set","messagePattern":"Mark not set","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/keyboard/vim.js","lineNumber":6128,"sourceCode":"\n        return result;\n      },\n      parseLineSpec_: function(cm, inputStream) {\n        var numberMatch = inputStream.match(/^(\\d+)/);\n        if (numberMatch) {\n          // Absolute line number plus offset (N+M or N-M) is probably a typo,\n          // not something the user actually wanted. (NB: vim does allow this.)\n          return parseInt(numberMatch[1], 10) - 1;\n        }\n        switch (inputStream.next()) {\n          case '.':\n            return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);\n          case '$':\n            return this.parseLineSpecOffset_(inputStream, cm.lastLine());\n          case '\\'':\n            var markName = inputStream.next();\n            var markPos = getMarkPos(cm, cm.state.vim, markName);\n            if (!markPos) throw new Error('Mark not set');\n            return this.parseLineSpecOffset_(inputStream, markPos.line);\n          case '-':\n          case '+':\n            inputStream.backUp(1);\n            // Offset is relative to current line if not otherwise specified.\n            return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);\n          default:\n            inputStream.backUp(1);\n            return undefined;\n        }\n      },\n      parseLineSpecOffset_: function(inputStream, line) {\n        var offsetMatch = inputStream.match(/^([+-])?(\\d+)/);\n        if (offsetMatch) {\n          var offset = parseInt(offsetMatch[2], 10);\n          if (offsetMatch[1] == \"-\") {\n            line -= offset;\n          } else {","sourceCodeStart":6110,"sourceCodeEnd":6146,"githubUrl":"https://github.com/ajaxorg/ace/blob/2c1eddc392eb92cf222a4478f1893d2b2c354fb0/src/keyboard/vim.js#L6110-L6146","documentation":"In vim ex command line specs, a mark reference ('x) looks up a previously set mark via getMarkPos. If the named mark was never set (or was invalidated), the position lookup returns null and the parser throws 'Mark not set', mirroring Vim's own behavior.","triggerScenarios":"Executing an ex range/command using a mark like :'a,'y s/x/y/ when mark 'a' was never set with ma; marks cleared by edits that deleted the marked line.","commonSituations":"Recording macros or remaps that assume marks exist; running commands on a freshly opened buffer where marks are unset.","solutions":["Set the mark first in normal mode (e.g. ma for mark a)","Use a different line spec (%, ., $) that doesn't rely on marks","Guard the command execution with a check that the mark exists"],"exampleFix":"// before\n:’a,’b s/foo/bar/g  // marks never set -> throws\n// after\n:normal ma  // set mark a first\n:'a,'b s/foo/bar/g","handlingStrategy":"validation","validationCode":"// Ensure marks exist before running mark-based ranges\nvar marksSet = ['a', 'b'].every(function(m) {\n  return !!window.__getMark && !!window.__getMark(m); // or track marks in your macro runner\n});\nif (!marksSet) return; // skip command or set marks first","typeGuard":"function markExists(cm, name) { try { return !!cm.ace.session; } catch (e) { return false; } } // verify via editor state before mark-based ranges","tryCatchPattern":"try { executeEx(\":'a,'b s/x/y/g\"); } catch (e) { if (/Mark not set/.test(e.message)) { /* set marks or use % range */ } else throw e; }","preventionTips":["Set marks (ma) before mark-based commands","Avoid mark ranges in macros for fresh buffers","Prefer % or absolute line ranges when marks are optional"],"tags":["vim","marks","ex-command"],"backgroundTag":"vim-mark-not-set","analyzedSha":"2c1eddc392eb92cf222a4478f1893d2b2c354fb0","analyzedAt":"2026-08-30T00:39:52.222Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}