Hmbown/CodeWhale · error · anyhow::Error
classifier-approved Git read did not keep its subcommand in
Error message
classifier-approved Git read did not keep its subcommand in argv[1]
What it means
After skipping the admitted preamble, the hardener only knows how to harden git's read subcommands: `diff`, `log`, `show` (get flag splices) plus `status`, `ls-files`, `blame`, `grep` (pass through bare). Any other subcommand reaches this refusal — the classifier admitted something the hardener cannot prove safe, so the path fails closed. The message text says 'argv[1]' but the check runs at the computed subcommand position after `-C`/`--no-pager`.
Source
Thrown at crates/tui/src/tools/shell.rs:3768
argv.splice(
at..at,
["--no-ext-diff".to_string(), "--no-textconv".to_string()],
);
}
"log" | "show" => {
let at = subcommand_index + 1;
argv.splice(
at..at,
[
"--no-ext-diff".to_string(),
"--no-textconv".to_string(),
"--no-show-signature".to_string(),
],
);
}
"status" | "ls-files" | "blame" | "grep" => {}
_ => {
return Err(anyhow!(
"classifier-approved Git read did not keep its subcommand in argv[1]"
));
}
}
}
let program = argv.remove(0);
Ok((program, argv))
}
fn enforce_readonly_workspace_operands(
command: &str,
workspace: &std::path::Path,
effective_cwd: &std::path::Path,
) -> Result<(), ToolError> {
let argv = shell_words::split(command).map_err(|error| {
ToolError::invalid_input(format!(
"Could not parse read-only command arguments: {error}"View on GitHub (pinned to 0c42157ee5)
Solutions
- Restrict agent git reads to the hardened set: diff/log/show/status/ls-files/blame/grep
- Route other git reads through a normal (approved, non-readonly) shell call or the File tools
- Report the admitted-but-unhardened subcommand as a classifier/hardener mismatch bug
- For `remote -v` style info, prefer `gh` reads or configuration inspection via allowed tools
Example fix
# before: subcommand outside the hardened set git -C repo remote -v # after: hardened read, or an approved full shell call git -C repo status # or dispatch 'git remote -v' through the non-readonly path
Defensive patterns
Strategy: validation
Validate before calling
const HARDENED_GIT_READS: &[&str] = &["diff", "log", "show", "status", "ls-files", "blame", "grep"];
fn git_subcommand_if_any(argv: &[String]) -> Option<&str> {
if argv.first().map(String::as_str) != Some("git") {
return None;
}
let mut i = 1;
while let Some(flag) = argv.get(i) {
match flag.as_str() {
"--no-pager" => i += 1,
"-C" => i += 2,
_ => break,
}
}
argv.get(i).map(String::as_str)
}
if let Some(sub) = git_subcommand_if_any(&argv)
&& !HARDENED_GIT_READS.contains(&sub)
{
return report(format!("git {sub} is outside the hardened read set; use an approved full shell call"));
} Type guard
fn hardenable_git_read(command: &str) -> bool {
let Ok(argv) = shell_words::split(command) else { return false };
if argv.first().map(String::as_str) != Some("git") { return true; }
let mut i = 1;
while let Some(flag) = argv.get(i) {
match flag.as_str() {
"--no-pager" => i += 1,
"-C" => i += 2,
_ => break,
}
}
matches!(
argv.get(i).map(String::as_str),
Some("diff" | "log" | "show" | "status" | "ls-files" | "blame" | "grep")
)
} Try / catch
match hardened_readonly_argv(command) {
Ok(parsed) => Ok(parsed),
Err(err) if err.to_string().contains("did not keep its subcommand") => {
report("switch to a hardened git read (diff/log/show/status/ls-files/blame/grep) or use an approved full shell call")
}
Err(err) => Err(err),
} Prevention
- Restrict agent git reads to diff/log/show/status/ls-files/blame/grep
- Teach command generators the hardened subcommand set
- Route novel git subcommands through the approval-gated non-readonly path
- Report classifier-admitted subcommands that reach this refusal as drift
When it happens
Trigger: A classifier-admitted git read whose subcommand falls outside {diff, log, show, status, ls-files, blame, grep} — e.g. `git -C dir remote -v`, `git describe --tags`, `git shortlog` — reaching the match's `_` arm at shell.rs:3768.
Common situations: Classifier allowlist drift admitting new git subcommands; agents probing less-common read-only git verbs that look harmless.
Related errors
- classifier-approved Git read was missing its literal subcomm
- read-only command must name a bare allowlisted executable
- could not parse classifier-approved read command: {error}
- classifier-approved read command was empty
- no trusted executable search path remains outside the worksp
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/2e9502c36bfd8ace.
Report an issue: GitHub.