{"record":{"id":"b6abef83454bf297","repo":"Hmbown/CodeWhale","slug":"selection-out-of-range","errorCode":null,"errorMessage":"Selection out of range","messagePattern":"Selection out of range","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"info","filePath":"crates/tui/src/lib.rs","lineNumber":7816,"sourceCode":"    println!(\"Select a session to resume:\");\n    for (idx, session) in sessions.iter().enumerate() {\n        println!(\"  {:>2}. {} ({})\", idx + 1, session.title, session.id);\n    }\n    print!(\"Enter a number (or press Enter to cancel): \");\n    io::stdout().flush()?;\n\n    let mut input = String::new();\n    io::stdin().read_line(&mut input)?;\n    let input = input.trim();\n    if input.is_empty() {\n        bail!(\"No session selected.\");\n    }\n    let idx: usize = input\n        .parse()\n        .map_err(|_| anyhow::anyhow!(\"Invalid input\"))?;\n    let session = sessions\n        .get(idx.saturating_sub(1))\n        .ok_or_else(|| anyhow::anyhow!(\"Selection out of range\"))?;\n    Ok(session.id.clone())\n}\n\nasync fn run_review(config: &Config, args: ReviewArgs) -> Result<()> {\n    use crate::client::DeepSeekClient;\n\n    let diff = collect_diff(&args)?;\n    if diff.trim().is_empty() {\n        bail!(\"No diff to review.\");\n    }\n    validate_review_receipt_args(&args)?;\n    if args.check_receipt {\n        return run_review_receipt_check(&diff, &args);\n    }\n\n    let model = resolve_review_model(config, args.model.as_deref());\n    let route = resolve_cli_exec_route(config, &model, &diff, args.model.is_none()).await?;\n    let execution_config = config_for_cli_route(config, &route);","sourceCodeStart":7798,"sourceCodeEnd":7834,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/lib.rs#L7798-L7834","documentation":"The interactive session picker maps the entered 1-based number to the list via sessions.get(idx.saturating_sub(1)). If the number exceeds the list length, the lookup fails with 'Selection out of range'. Note the saturating_sub quirk: entering 0 wraps to index 0 and silently selects the first session instead of erroring.","triggerScenarios":"Entering a number larger than the count of listed sessions (for example 7 when 3 are shown); the list shrank between display and input; reading a stale longer list from scrollback. Entering 0 does not error — it selects the first session.","commonSituations":"The terminal shows a longer list from an earlier run in scrollback; a typo adds a digit; a script pipes a fixed index that outlived the session count.","solutions":["Re-check the printed list and enter a number between 1 and the number of sessions","Clear the screen or scrollback and re-run so the current list is visible","In scripts, resume by explicit session ID instead of a positional pick","If you entered 0, be aware it opened the first session — verify which session you are in"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let n = sessions.len();\nmatch input.trim().parse::<usize>() {\n    Ok(i) if (1..=n).contains(&i) => { /* safe to select */ }\n    _ => eprintln!(\"enter a number from 1 to {n}\"),\n}","typeGuard":"fn selection_in_range(input: &str, len: usize) -> bool {\n    matches!(input.trim().parse::<usize>(), Ok(i) if (1..=len).contains(&i))\n}","tryCatchPattern":null,"preventionTips":["Count the printed entries before typing the number","Do not rely on scrolled-back lists from earlier runs","Remember 0 selects the first session rather than erroring","Pin explicit session IDs in scripts"],"tags":["rust","cli","sessions","input-validation"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}