{"record":{"id":"428879ee3fe4cb1e","repo":"gitbutlerapp/gitbutler","slug":"could-not-find-range-from-to-in-the-disp","errorCode":null,"errorMessage":"Could not find range from '{}' to '{}' in the displayed file list","messagePattern":"Could not find range from '(.+?)' to '(.+?)' in the displayed file list","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but/src/id/parser.rs","lineNumber":173,"sourceCode":"    // Both sides must resolve to exactly one Uncommitted entity\n    if start_matches.len() != 1 || end_matches.len() != 1 {\n        return Ok(None);\n    }\n    if !matches!(&start_matches[0], CliId::UncommittedHunkOrFile(_))\n        || !matches!(&end_matches[0], CliId::UncommittedHunkOrFile(_))\n    {\n        return Ok(None);\n    }\n\n    // Valid range — resolve positions in display order\n    let all_files = get_all_files_in_display_order(id_map)?;\n    let start_pos = all_files.iter().position(|id| id == &start_matches[0]);\n    let end_pos = all_files.iter().position(|id| id == &end_matches[0]);\n\n    match (start_pos, end_pos) {\n        (Some(s), Some(e)) if s <= e => Ok(Some(all_files[s..=e].to_vec())),\n        (Some(s), Some(e)) => Ok(Some(all_files[e..=s].to_vec())),\n        _ => Err(anyhow::anyhow!(\n            \"Could not find range from '{}' to '{}' in the displayed file list\",\n            parts[0],\n            parts[1]\n        )),\n    }\n}\n\nfn get_all_files_in_display_order(id_map: &IdMap) -> anyhow::Result<Vec<CliId>> {\n    let mut files: Vec<(&BStr, CliId)> = id_map\n        .uncommitted_files\n        .values()\n        .map(|uncommitted_file| (uncommitted_file.path(), uncommitted_file.to_id()))\n        .collect();\n    files.sort_by_key(|(a_path, _)| *a_path);\n\n    Ok(files.into_iter().map(|(_, cli_id)| cli_id).collect())\n}\n","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/id/parser.rs#L155-L191","documentation":"The but ID parser accepts range selections like '<start>..<end>' over the file IDs currently displayed. After parsing, both endpoint IDs are looked up in get_all_files_in_display_order; if either endpoint is not in that list, the range cannot be resolved and this error is thrown.","triggerScenarios":"Issuing a command with a range argument (e.g. file1..file2) where one endpoint ID does not exist in the current display: a stale ID from an earlier render, a typo, or the file list changed between listing and command.","commonSituations":"Copy-pasting IDs from an outdated `but status` output; scripts that cache IDs across runs; files staged/hidden between render and command.","solutions":["Re-run the listing (e.g. `but status`) to refresh the displayed IDs and redo the selection","Verify both endpoints appear in the current list before using a range","Use individual IDs instead of a range when the list is volatile"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let ids = get_all_files_in_display_order(&id_map)?;\nlet endpoints_ok = [start_id, end_id].iter().all(|id| ids.iter().any(|i| i == id));\nanyhow::ensure!(endpoints_ok, \"range endpoints not in the current file list; re-render\");","typeGuard":null,"tryCatchPattern":"Treat this error as 'stale selection': re-fetch the current ID list, re-map the user's selection onto fresh IDs, and retry once instead of surfacing the raw error.","preventionTips":["Never cache displayed IDs across renders","Re-render the list immediately before applying a range selection"],"tags":["id-parser","range-selection","stale-state","cli"],"backgroundTag":"selection-id-not-found","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}