{"record":{"id":"94ba9587f14f9550","repo":"zeroclaw-labs/zeroclaw","slug":"runtime-shell-must-not-be-empty-or-whitespace","errorCode":null,"errorMessage":"runtime.shell must not be empty or whitespace","messagePattern":"runtime\\.shell must not be empty or whitespace","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/platform/mod.rs","lineNumber":38,"sourceCode":"        RuntimeKind::Docker => Ok(Box::new(DockerRuntime::new(config.docker.clone()))),\n        RuntimeKind::Cloudflare => anyhow::bail!(\n            \"runtime.kind='cloudflare' is not implemented yet. Use runtime.kind='native' for now.\"\n        ),\n    }\n}\n\n#[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            ),","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/platform/mod.rs#L20-L56","documentation":"The unix validate_shell rejects runtime.shell values that are empty or whitespace-only, because spawning would otherwise run with no program. Note the Android carve-out just above the check: on Android the whole validation is skipped since the shell is located at spawn time.","triggerScenarios":"runtime.shell = \"\" or \"   \" on non-Android unix when create_runtime validates the shell before building a NativeRuntime.","commonSituations":"Template configs shipped with an empty shell field; templating that writes shell = \"${SHELL}\" with the variable unset; a truncated TOML edit leaving whitespace.","solutions":["Set a real shell: a bare name (\"bash\") or an absolute path (\"/bin/bash\").","Remove the runtime.shell key to use the runtime default.","If the value comes from env expansion, give it a fallback such as ${SHELL:-/bin/bash}."],"exampleFix":"# before\n[runtime]\nshell = \"\"\n\n# after\n[runtime]\nshell = \"/bin/bash\"","handlingStrategy":"validation","validationCode":"#[cfg(not(target_os = \"android\"))]\nif cfg.runtime_shell.map_or(false, |s| s.trim().is_empty()) {\n    return Err(anyhow::anyhow!(\"runtime.shell must not be empty\"));\n}","typeGuard":"fn is_nonempty_shell(value: &str) -> bool {\n    !value.trim().is_empty()\n}","tryCatchPattern":"match create_runtime(&config) {\n    Err(e) if e.to_string().contains(\"must not be empty\") => {\n        // fill a concrete shell (\"/bin/bash\") or drop the key and retry\n    }\n    other => other,\n}","preventionTips":["Omit runtime.shell rather than shipping an empty value.","Give templated env-derived values a fallback (e.g. ${SHELL:-/bin/bash}).","Lint generated configs for empty-string fields before deployment.","Remember Android skips this check entirely — do not rely on it there."],"tags":["runtime","shell","config","unix"],"backgroundTag":"shell-config-invalid","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}