Hmbown/CodeWhale · error
classifier-approved read command was empty
Error message
classifier-approved read command was empty
What it means
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.
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.
Example fix
// before "git log | | head" // middle segment is empty // after "git log | head"
Defensive patterns
Strategy: validation
Validate before calling
function hasEmptySegment(cmd: string): boolean {
if (!cmd.trim()) return true;
return cmd.split('|').some(seg => seg.trim() === '');
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- base URL cannot be empty
- browser URL cannot be empty
- Codewhale runtime event file is empty.
- could not parse classifier-approved read command
- CRON field must not be empty
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f4bf0df5be281bb0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/shell.rs:4190
.to_str()
.ok_or_else(|| anyhow!("read-only executable path is not valid UTF-8"))?;
Ok(std::iter::once(program)
.chain(args.iter().map(String::as_str))
.map(|arg| shell_words::quote(arg).into_owned())
.collect::<Vec<_>>()
.join(" "))
})
.collect::<Result<Vec<_>>>()?;
// The only shell operators are our pipes. Filenames cannot expand into
// options or unvalidated symlinks; every Git stage retains helper guards.
Ok(format!("set -o pipefail; {}", segments.join(" | ")))
}
fn hardened_readonly_argv(command: &str) -> Result<(String, Vec<String>)> {
let mut argv = shell_words::split(&normalize_windows_command_paths(command))
.map_err(|error| anyhow!("could not parse classifier-approved read command: {error}"))?;
if argv.is_empty() {
return Err(anyhow!("classifier-approved read command was empty"));
}
// Even when repository/user configuration names a diff or signature
// helper, these flags make Git keep the read inside its own process.
if argv.first().is_some_and(|program| program == "git") {
// The agent read-only classifier admits `git -C <dir>` and
// `git --no-pager` before the subcommand; keep the preamble but
// locate the subcommand after it so the hardening flags splice in
// the right place. `-C` targets were already workspace-checked by
// `enforce_readonly_workspace_operands`.
let mut subcommand_index = 1;
while let Some(flag) = argv.get(subcommand_index) {
match flag.as_str() {
"--no-pager" => subcommand_index += 1,
"-C" => subcommand_index += 2,
_ => break,
}
}View on GitHub (pinned to 73e0f67d83)