{"record":{"id":"8bbdac5265d9fc16","repo":"Hmbown/CodeWhale","slug":"read-only-pipelines-require-bash-or-zsh-run-each-read","errorCode":null,"errorMessage":"read-only pipelines require bash or zsh; run each read separately","messagePattern":"read-only pipelines require bash or zsh; run each read separately","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":4157,"sourceCode":"        .and_then(serde_json::Value::as_str)\n        .is_some()\n}\n\nfn hardened_readonly_pipeline(command: &str, workspace: &std::path::Path) -> Result<String> {\n    use crate::shell_dispatcher::ShellKind;\n    // POSIX quoting must never be passed to a different command interpreter.\n    let supported = match crate::shell_dispatcher::global_dispatcher().kind() {\n        ShellKind::Bash => true,\n        ShellKind::Custom { binary, .. } => matches!(\n            std::path::Path::new(binary)\n                .file_name()\n                .and_then(|name| name.to_str()),\n            Some(\"bash\" | \"zsh\")\n        ),\n        _ => false,\n    };\n    if !supported {\n        return Err(anyhow!(\n            \"read-only pipelines require bash or zsh; run each read separately\"\n        ));\n    }\n    if !is_agent_readonly_shell_command(command) {\n        return Err(anyhow!(\n            \"pipeline contains a command outside the read-only policy\"\n        ));\n    }\n    let segments = command\n        .split('|')\n        .map(|segment| {\n            let (program, args) = hardened_readonly_argv(segment)?;\n            let program = resolve_readonly_program(&program, workspace)?;\n            let program = program\n                .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))","sourceCodeStart":4139,"sourceCodeEnd":4175,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L4139-L4175","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Set your SHELL environment variable (or the tool's shell configuration) to /bin/bash or /usr/bin/zsh before invoking the agent.","Split the pipeline into separate single read-only commands and run each one individually, as the message suggests.","On minimal containers, install bash or create a symlink so a bash binary is available and selected."],"exampleFix":"// before\n$ SHELL=/bin/sh codewhale\nagent: git log | head -20\n// after\n$ SHELL=/bin/bash codewhale\nagent: git log | head -20\n// or split\nagent: git log -20  # run separately instead of piping to head","handlingStrategy":"validation","validationCode":"const shell = process.env.SHELL ?? '';\nconst base = shell.split('/').pop() ?? '';\nif (base !== 'bash' && base !== 'zsh') {\n  // run each read command separately instead of a pipeline\n}","typeGuard":"function isSupportedShell(shell: string | undefined): boolean {\n  const base = (shell ?? '').split('/').pop();\n  return base === 'bash' || base === 'zsh';\n}","tryCatchPattern":null,"preventionTips":["Launch the agent with SHELL=/bin/bash (or zsh) in containers and CI.","Avoid login shells like fish/dash when using the agent's pipeline reads.","Prefer single non-piped read commands when shell support is uncertain."],"tags":["shell","read-only","pipeline","bash","zsh"],"backgroundTag":"unsupported-operation","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"}