{"record":{"id":"f4bf0df5be281bb0","repo":"Hmbown/CodeWhale","slug":"classifier-approved-read-command-was-empty","errorCode":null,"errorMessage":"classifier-approved read command was empty","messagePattern":"classifier-approved read command was empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":4190,"sourceCode":"                .to_str()\n                .ok_or_else(|| anyhow!(\"read-only executable path is not valid UTF-8\"))?;\n            Ok(std::iter::once(program)\n                .chain(args.iter().map(String::as_str))\n                .map(|arg| shell_words::quote(arg).into_owned())\n                .collect::<Vec<_>>()\n                .join(\" \"))\n        })\n        .collect::<Result<Vec<_>>>()?;\n    // The only shell operators are our pipes. Filenames cannot expand into\n    // options or unvalidated symlinks; every Git stage retains helper guards.\n    Ok(format!(\"set -o pipefail; {}\", segments.join(\" | \")))\n}\n\nfn hardened_readonly_argv(command: &str) -> Result<(String, Vec<String>)> {\n    let mut argv = shell_words::split(&normalize_windows_command_paths(command))\n        .map_err(|error| anyhow!(\"could not parse classifier-approved read command: {error}\"))?;\n    if argv.is_empty() {\n        return Err(anyhow!(\"classifier-approved read command was empty\"));\n    }\n\n    // Even when repository/user configuration names a diff or signature\n    // helper, these flags make Git keep the read inside its own process.\n    if argv.first().is_some_and(|program| program == \"git\") {\n        // The agent read-only classifier admits `git -C <dir>` and\n        // `git --no-pager` before the subcommand; keep the preamble but\n        // locate the subcommand after it so the hardening flags splice in\n        // the right place. `-C` targets were already workspace-checked by\n        // `enforce_readonly_workspace_operands`.\n        let mut subcommand_index = 1;\n        while let Some(flag) = argv.get(subcommand_index) {\n            match flag.as_str() {\n                \"--no-pager\" => subcommand_index += 1,\n                \"-C\" => subcommand_index += 2,\n                _ => break,\n            }\n        }","sourceCodeStart":4172,"sourceCodeEnd":4208,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L4172-L4208","documentation":"After shell_words parsing succeeds, hardened_readonly_argv rejects an empty argv. A command that tokenizes to zero words carries no program to execute, so the read-only pipeline builder cannot construct a stage for it and fails loudly rather than emitting a meaningless empty command.","triggerScenarios":"Passing an empty string, a whitespace-only string, or a segment that reduces to nothing (e.g. a pipeline like 'a | | b' producing an empty segment, or a comment-only string like '# nothing') as a classifier-approved read command.","commonSituations":"Malformed pipelines with doubled pipes creating empty segments; the agent model emitting blank or whitespace-only commands; command strings that consist solely of quotes stripped away during tokenization.","solutions":["Supply a non-empty read-only command (e.g. 'git status', 'ls').","Fix doubled or trailing pipe characters in the pipeline so no segment is empty ('a | b', not 'a | | b').","If the model produced a blank command, retry the request to get a real command."],"exampleFix":"// before\n\"git log | | head\"   // middle segment is empty\n// after\n\"git log | head\"","handlingStrategy":"validation","validationCode":"function hasEmptySegment(cmd: string): boolean {\n  if (!cmd.trim()) return true;\n  return cmd.split('|').some(seg => seg.trim() === '');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check for doubled or trailing pipe characters before submitting pipelines.","Never pass empty or whitespace-only commands to the shell tool.","Retry when the model emits a blank command."],"tags":["shell","empty-input","pipeline","validation"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}