Hmbown/CodeWhale · error

could not parse classifier-approved read command

Error message

could not parse classifier-approved read command: {error}

What it means

hardened_readonly_argv tokenizes the classifier-approved command using shell_words::split (POSIX shell word splitting) after Windows path normalization. If the command string is not valid POSIX shell syntax for splitting (unbalanced quotes, trailing backslash, etc.), the tool cannot safely decompose it into argv and rejects it.

Solutions

  1. Re-issue the command with balanced, properly escaped quoting — wrap arguments containing spaces or special characters in double quotes.
  2. Remove problematic characters (apostrophes, trailing backslashes) from arguments, or use simpler glob-free arguments.
  3. If this comes from model output, retry the request so the model regenerates a syntactically valid command.

Example fix

// before (unbalanced quote)
git log --format='%h %s it's a commit | head
// after
git log --format="%h %s" | head
Defensive patterns

Strategy: validation

Validate before calling

function quotesBalanced(cmd: string): boolean {
  let s = false, d = false;
  for (let i = 0; i < cmd.length; i++) {
    const c = cmd[i];
    if (c === '\\') { i++; continue; }
    if (c === "'" && !d) s = !s;
    if (c === '"' && !s) d = !d;
  }
  return !s && !d && !cmd.trimEnd().endsWith('\\');
}

Prevention

When it happens

Trigger: A pipeline segment contains shell syntax shell_words::split rejects: unbalanced single/double quotes, an unterminated escape like 'foo\', or other malformed quoting in the command the classifier approved.

Common situations: The agent model emits a command with a stray apostrophe (e.g. git log --format='%h | it's fine'); generated commands interpolating user text with quotes; Windows-style paths with backslashes misinterpreted as escapes.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/9378c0e23eb42122. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/shell.rs:4188

            let program = resolve_readonly_program(&program, workspace)?;
            let program = program
                .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)