zeroclaw-labs/zeroclaw · error · anyhow::Error
runtime.shell {shell:?} is a relative path; use a bare name
Error message
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") What it means
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.
Source
Thrown at crates/zeroclaw-config/src/platform/mod.rs:45
#[cfg(unix)]
fn validate_shell(shell: &str) -> anyhow::Result<()> {
use std::os::unix::fs::PermissionsExt;
// Android pins the shell to /system/bin/sh; the configured value is never
// used, so don't reject it.
if zeroclaw_api::platform::is_android() {
return Ok(());
}
if shell.trim().is_empty() {
anyhow::bail!("runtime.shell must not be empty or whitespace");
}
let path = std::path::Path::new(shell);
let resolved = if path.is_absolute() {
path.to_path_buf()
} else if path.components().count() > 1 {
anyhow::bail!(
"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\")"
);
} else {
match std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default())
.map(|dir| dir.join(shell))
.find(|candidate| candidate.is_file())
{
Some(found) => found,
None => anyhow::bail!(
"runtime.shell {shell:?} was not found on PATH; use an absolute path or install the shell"
),
}
};
if !resolved.exists() {
anyhow::bail!(
"runtime.shell {shell:?} (resolved to {}) does not exist",
resolved.display()View on GitHub (pinned to 88bb9c8533)
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.
Example fix
# before [runtime] shell = "bin/bash" # after [runtime] shell = "/opt/toolchain/bin/bash" # or just "bash" for PATH resolution
Defensive patterns
Strategy: validation
Validate before calling
let p = std::path::Path::new(shell);
let ok = p.is_absolute() || p.components().count() == 1; // absolute or bare name
if !ok { /* reject before create_runtime: use "bash" or "/bin/bash" */ } Type guard
fn is_valid_shell_form(shell: &str) -> bool {
let p = std::path::Path::new(shell);
!shell.trim().is_empty() && (p.is_absolute() || p.components().count() == 1)
} Try / catch
match create_runtime(&config) {
Err(e) if e.to_string().contains("is a relative path") => {
// rewrite as a bare name or absolute path and retry
}
other => other,
} Prevention
- Only two forms are valid: bare name or absolute path — never ./x or a/b.
- Resolve project-local shells to absolute paths at config-generation time.
- Add a lint rule rejecting multi-component non-absolute shell values.
When it happens
Trigger: runtime.shell = "bin/bash", "./zsh", or "shells/fish" on unix — any value that is not absolute and has more than one path component.
Common situations: 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.
Related errors
- runtime.shell must not be empty or whitespace
- runtime.shell {shell:?} was not found on PATH; use an absolu
- runtime.shell {shell:?} (resolved to {}) does not exist
- runtime.shell {shell:?} (resolved to {}) could not be inspec
- runtime.shell {shell:?} (resolved to {}) is not a regular fi
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/81d693a01c112d63.
Report an issue: GitHub.