{"record":{"id":"0471bcde772058d8","repo":"sxyazi/yazi","slug":"the-cursor-position-is-out-of-bounds","errorCode":null,"errorMessage":"The cursor position is out of bounds.","messagePattern":"The cursor position is out of bounds\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"yazi-parser/src/mgr/shell.rs","lineNumber":30,"sourceCode":"\n\t#[serde(default)]\n\tpub block:       bool,\n\t#[serde(default)]\n\tpub orphan:      bool,\n\t#[serde(default)]\n\tpub interactive: bool,\n\n\tpub cursor: Option<usize>,\n}\n\nimpl TryFrom<ActionCow> for ShellForm {\n\ttype Error = anyhow::Error;\n\n\tfn try_from(a: ActionCow) -> Result<Self, Self::Error> {\n\t\tlet me: Self = a.deserialize()?;\n\n\t\tif me.cursor.is_some_and(|c| c > me.run.chars().count()) {\n\t\t\tbail!(\"The cursor position is out of bounds.\");\n\t\t}\n\n\t\tOk(me)\n\t}\n}\n\nimpl FromLua for ShellForm {\n\tfn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err(\"unsupported\".into_lua_err()) }\n}\n\nimpl IntoLua for ShellForm {\n\tfn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err(\"unsupported\".into_lua_err()) }\n}\n","sourceCodeStart":12,"sourceCodeEnd":44,"githubUrl":"https://github.com/sxyazi/yazi/blob/5f901b886b14de1f17460b6e52e9de5d67f8aba9/yazi-parser/src/mgr/shell.rs#L12-L44","documentation":"The `shell` action form validates the optional `cursor` field: if set, it must be ≤ the character count of the command string (`run`). A larger value means the caret would sit past the end of the input, so construction bails with this error.","triggerScenarios":"Emitting the `mgr:shell` action with a `cursor` value greater than the number of chars in `run` — e.g. `run = \"ls\"` with `cursor = 5`, or a stale cursor carried over from a longer previous command.","commonSituations":"Plugin state that remembers the caret position but updates the command to a shorter string; byte-vs-char confusion with multibyte commands (validation counts chars, not bytes); hand-built keymap bindings with hardcoded cursors.","solutions":["Clamp cursor to `run.chars().count()` before emitting the action.","Omit cursor (None) to let the shell action default the position.","Compute the cursor with the same char semantics (`chars().count()` / Lua `#` on chars) used by the validator, not byte length."],"exampleFix":"// before\nlet cursor = Some(old_pos);\nShellForm::from_action(run, cursor)\n// after\nlet n = run.chars().count();\nlet cursor = Some(old_pos.min(n));","handlingStrategy":"validation","validationCode":"assert!(cursor.map_or(true, |c| c <= run.chars().count()),\n        \"cursor must be <= run.chars().count()\");","typeGuard":"fn is_valid_cursor(run: &str, cursor: Option<usize>) -> bool {\n    cursor.map_or(true, |c| c <= run.chars().count())\n}","tryCatchPattern":"match ShellForm::try_from(action) {\n    Ok(form) => open(form),\n    Err(e) if e.to_string().contains(\"cursor position is out of bounds\") => {\n        eprintln!(\"cursor beyond end of command: {e}\")\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Clamp stored cursors whenever the command text shrinks.","Count characters (chars().count()), never bytes, when computing cursor positions.","Prefer omitting the cursor field unless you track it accurately."],"tags":["parser","shell","bounds-validation"],"backgroundTag":"cursor-position-out-of-bounds","analyzedSha":"5f901b886b14de1f17460b6e52e9de5d67f8aba9","analyzedAt":"2026-09-02T18:38:25.566Z","contentChangedAt":"2026-09-02T18:38:25.566Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}