{"record":{"id":"130dad1f92566b58","repo":"spacedriveapp/spacedrive","slug":"invalid-choice-selected","errorCode":null,"errorMessage":"Invalid choice selected","messagePattern":"Invalid choice selected","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"apps/cli/src/domains/file/mod.rs","lineNumber":162,"sourceCode":"\n\t\t\t// Apply the user's choice to the input\n\t\t\tmatch choice_index {\n\t\t\t\t0 => {\n\t\t\t\t\t// Overwrite: set conflict resolution in input\n\t\t\t\t\tuse sd_core::ops::files::copy::action::FileConflictResolution;\n\t\t\t\t\tinput.on_conflict = Some(FileConflictResolution::Overwrite);\n\t\t\t\t}\n\t\t\t\t1 => {\n\t\t\t\t\t// Auto-rename: set conflict resolution in input\n\t\t\t\t\tuse sd_core::ops::files::copy::action::FileConflictResolution;\n\t\t\t\t\tinput.on_conflict = Some(FileConflictResolution::AutoModifyName);\n\t\t\t\t}\n\t\t\t\t2 => {\n\t\t\t\t\t// Abort\n\t\t\t\t\tanyhow::bail!(\"Operation aborted by user\");\n\t\t\t\t}\n\t\t\t\t_ => {\n\t\t\t\t\tanyhow::bail!(\"Invalid choice selected\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Execute the action using the input\n\tlet job_id: JobId = execute_action!(ctx, input);\n\tOk(job_id)\n}\n\n/// Simple conflict detection for CLI\nasync fn check_for_simple_conflicts(\n\taction: &sd_core::ops::files::copy::action::FileCopyAction,\n) -> Result<bool> {\n\tuse sd_core::domain::addressing::SdPath;\n\n\t// Extract the physical path from the destination SdPath\n\tlet dest_path = match &action.destination {","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/spacedriveapp/spacedrive/blob/6dfeccf2113039e35f2ce735f945e70dc3e4ea45/apps/cli/src/domains/file/mod.rs#L144-L180","documentation":"After the conflict prompt, the match handles indices 0 (Overwrite), 1 (AutoModifyName), and 2 (Abort), with a catch-all `_` arm bailing 'Invalid choice selected'. The prompt implementation (`prompt_for_choice` in apps/cli/src/util/confirm.rs:37) loops until it gets a number in 1..=len and returns a 0-based index, so with the standard three choices this arm is unreachable. Hitting it means the choice list was changed (more resolutions added to FileConflictResolution::CHOICES without updating the match) or the prompt was bypassed in testing.","triggerScenarios":"A maintainer adds a fourth FileConflictResolution variant; CHOICES grows, prompt returns index 3, and the match's `_` arm fires. Cannot be triggered by typing anything at the real prompt.","commonSituations":"Regression after extending conflict-resolution options; test harnesses that stub prompt_for_choice and return out-of-range indices.","solutions":["For users: this is a code defect, not an input mistake — report it with the CLI version.","For maintainers: replace numeric indices with an exhaustive match over the choices, e.g. map FileConflictResolution::from_index(idx) and add a unit test covering len(CHOICES).","Verify any local patches to confirm.rs or the CHOICES list are in sync."],"exampleFix":"// before: numeric arms, silently breaks when CHOICES grows\nmatch prompt_for_choice(request)? { 0 => ..., 1 => ..., 2 => ..., _ => anyhow::bail!(\"Invalid choice selected\") }\n\n// after: exhaustively map every registered choice\nlet idx = prompt_for_choice(request)?;\nlet resolution = FileConflictResolution::CHOICES.get(idx).copied().ok_or_else(|| anyhow::anyhow!(\"Unknown conflict choice at index {}\", idx))?;\ninput.on_conflict = Some(resolution);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match run_copy_with_confirmation(ctx, input).await {\n    Err(e) if e.to_string().contains(\"Invalid choice selected\") => {\n        // unreachable via the real prompt — treat as a bug report, not user error\n        report_bug(\"conflict CHOICES out of sync with match arms\");\n        return Err(e);\n    }\n    other => other?,\n}","preventionTips":["When extending FileConflictResolution::CHOICES, update the match arms in the same commit and add a test over all indices.","Prefer exhaustive mapping via CHOICES.get(idx) over hardcoded numeric arms.","Test harnesses stubbing prompt_for_choice must clamp return values to 0..len(CHOICES)."],"tags":["cli","file-copy","conflict","unreachable-code","defensive-code"],"backgroundTag":null,"analyzedSha":"6dfeccf2113039e35f2ce735f945e70dc3e4ea45","analyzedAt":"2026-08-16T11:26:17.074Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}