Hmbown/CodeWhale · error

read-only pipelines require bash or zsh; run each read…

Error message

read-only pipelines require bash or zsh; run each read separately

What it means

The agent read-only shell pipeline path in crates/tui/src/tools/shell.rs only supports bash and zsh. When it inspects the invoking shell (e.g. via $SHELL) and finds any other name, it refuses to execute the pipeline as a single read-only command. This prevents reinterpreting shell-specific pipeline syntax under a shell whose semantics the read-only policy was not validated against.

Solutions

  1. Set your SHELL environment variable (or the tool's shell configuration) to /bin/bash or /usr/bin/zsh before invoking the agent.
  2. Split the pipeline into separate single read-only commands and run each one individually, as the message suggests.
  3. On minimal containers, install bash or create a symlink so a bash binary is available and selected.

Example fix

// before
$ SHELL=/bin/sh codewhale
agent: git log | head -20
// after
$ SHELL=/bin/bash codewhale
agent: git log | head -20
// or split
agent: git log -20  # run separately instead of piping to head
Defensive patterns

Strategy: validation

Validate before calling

const shell = process.env.SHELL ?? '';
const base = shell.split('/').pop() ?? '';
if (base !== 'bash' && base !== 'zsh') {
  // run each read command separately instead of a pipeline
}

Type guard

function isSupportedShell(shell: string | undefined): boolean {
  const base = (shell ?? '').split('/').pop();
  return base === 'bash' || base === 'zsh';
}

Prevention

When it happens

Trigger: Calling the agent read-only pipeline execution path with a command while the detected shell program is not 'bash' or 'zsh' (e.g. sh, dash, fish, nushell, pwsh, cmd, or an unset/non-UTF-8 SHELL that fails the Some("bash" | "zsh") match).

Common situations: Running Codewhale on systems where /bin/sh links to dash (Debian/Ubuntu) and SHELL is inherited as 'sh'; users whose login shell is fish, zsh-less minimal containers, or Windows where the command would run under cmd/powershell.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

        .and_then(serde_json::Value::as_str)
        .is_some()
}

fn hardened_readonly_pipeline(command: &str, workspace: &std::path::Path) -> Result<String> {
    use crate::shell_dispatcher::ShellKind;
    // POSIX quoting must never be passed to a different command interpreter.
    let supported = match crate::shell_dispatcher::global_dispatcher().kind() {
        ShellKind::Bash => true,
        ShellKind::Custom { binary, .. } => matches!(
            std::path::Path::new(binary)
                .file_name()
                .and_then(|name| name.to_str()),
            Some("bash" | "zsh")
        ),
        _ => false,
    };
    if !supported {
        return Err(anyhow!(
            "read-only pipelines require bash or zsh; run each read separately"
        ));
    }
    if !is_agent_readonly_shell_command(command) {
        return Err(anyhow!(
            "pipeline contains a command outside the read-only policy"
        ));
    }
    let segments = command
        .split('|')
        .map(|segment| {
            let (program, args) = hardened_readonly_argv(segment)?;
            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))

View on GitHub (pinned to 73e0f67d83)