{"record":{"id":"81d693a01c112d63","repo":"zeroclaw-labs/zeroclaw","slug":"runtime-shell-shell-is-a-relative-path-use-a","errorCode":null,"errorMessage":"runtime.shell {shell:?} is a relative path; use a bare name resolved on PATH (e.g. \"bash\") or an absolute path (e.g. \"/bin/bash\")","messagePattern":"runtime\\.shell (.+?) is a relative path; use a bare name resolved on PATH \\(e\\.g\\. \"bash\"\\) or an absolute path \\(e\\.g\\. \"/bin/bash\"\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/platform/mod.rs","lineNumber":45,"sourceCode":"#[cfg(unix)]\nfn validate_shell(shell: &str) -> anyhow::Result<()> {\n    use std::os::unix::fs::PermissionsExt;\n\n    // Android pins the shell to /system/bin/sh; the configured value is never\n    // used, so don't reject it.\n    if zeroclaw_api::platform::is_android() {\n        return Ok(());\n    }\n\n    if shell.trim().is_empty() {\n        anyhow::bail!(\"runtime.shell must not be empty or whitespace\");\n    }\n\n    let path = std::path::Path::new(shell);\n    let resolved = if path.is_absolute() {\n        path.to_path_buf()\n    } else if path.components().count() > 1 {\n        anyhow::bail!(\n            \"runtime.shell {shell:?} is a relative path; use a bare name resolved on PATH (e.g. \\\"bash\\\") or an absolute path (e.g. \\\"/bin/bash\\\")\"\n        );\n    } else {\n        match std::env::split_paths(&std::env::var_os(\"PATH\").unwrap_or_default())\n            .map(|dir| dir.join(shell))\n            .find(|candidate| candidate.is_file())\n        {\n            Some(found) => found,\n            None => anyhow::bail!(\n                \"runtime.shell {shell:?} was not found on PATH; use an absolute path or install the shell\"\n            ),\n        }\n    };\n\n    if !resolved.exists() {\n        anyhow::bail!(\n            \"runtime.shell {shell:?} (resolved to {}) does not exist\",\n            resolved.display()","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/platform/mod.rs#L27-L63","documentation":"validate_shell classifies the value three ways: absolute path, bare single-component name resolved on PATH, or everything else. Multi-component relative paths like \"bin/bash\" or \"./zsh\" fall into the third bucket and are rejected — they are ambiguous (relative to which cwd?) and resolvable by neither strategy.","triggerScenarios":"runtime.shell = \"bin/bash\", \"./zsh\", or \"shells/fish\" on unix — any value that is not absolute and has more than one path component.","commonSituations":"Writing ./bin/sh out of shell-script habit; referencing a project-local shell relative to the repo; config values assembled by joining a relative prefix with a binary name.","solutions":["Use a bare name (\"bash\") so PATH resolution applies, or an absolute path (\"/usr/local/bin/fish\").","For a project-local shell, compute and store its absolute path.","Resolve ./x forms against the cwd first, e.g. $(pwd)/x, before writing the config."],"exampleFix":"# before\n[runtime]\nshell = \"bin/bash\"\n\n# after\n[runtime]\nshell = \"/opt/toolchain/bin/bash\"   # or just \"bash\" for PATH resolution","handlingStrategy":"validation","validationCode":"let p = std::path::Path::new(shell);\nlet ok = p.is_absolute() || p.components().count() == 1; // absolute or bare name\nif !ok { /* reject before create_runtime: use \"bash\" or \"/bin/bash\" */ }","typeGuard":"fn is_valid_shell_form(shell: &str) -> bool {\n    let p = std::path::Path::new(shell);\n    !shell.trim().is_empty() && (p.is_absolute() || p.components().count() == 1)\n}","tryCatchPattern":"match create_runtime(&config) {\n    Err(e) if e.to_string().contains(\"is a relative path\") => {\n        // rewrite as a bare name or absolute path and retry\n    }\n    other => other,\n}","preventionTips":["Only two forms are valid: bare name or absolute path — never ./x or a/b.\n","Resolve project-local shells to absolute paths at config-generation time.","Add a lint rule rejecting multi-component non-absolute shell values."],"tags":["runtime","shell","relative-path","unix"],"backgroundTag":"shell-path-invalid","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}