{"record":{"id":"b9176ee2e5340304","repo":"zeroclaw-labs/zeroclaw","slug":"runtime-shell-shell-resolved-to-is-not-ex","errorCode":null,"errorMessage":"runtime.shell {shell:?} (resolved to {}) is not executable","messagePattern":"runtime\\.shell (.+?) \\(resolved to (.+?)\\) is not executable","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/platform/mod.rs","lineNumber":87,"sourceCode":"        Err(e) => anyhow::bail!(\n            \"runtime.shell {shell:?} (resolved to {}) could not be inspected: {e}\",\n            resolved.display()\n        ),\n    };\n    if !metadata.is_file() {\n        anyhow::bail!(\n            \"runtime.shell {shell:?} (resolved to {}) is not a regular file\",\n            resolved.display()\n        );\n    }\n\n    // Coarse check: reject only when no execute bit is set at all. A precise\n    // \"can *we* execute it\" test (uid/gid vs. the file owner) buys little —\n    // the kernel's spawn is the real authority (ACLs, caps, mount flags) — and\n    // this is a fail-fast sanity check, not a security gate.\n    let mode = metadata.permissions().mode();\n    if mode & 0o111 == 0 {\n        anyhow::bail!(\n            \"runtime.shell {shell:?} (resolved to {}) is not executable\",\n            resolved.display()\n        );\n    }\n\n    Ok(())\n}\n\n/// Validate a configured `runtime.shell` on Windows.\n///\n/// Unlike the Unix check this does not resolve a binary on `PATH`: on Windows\n/// `runtime.shell` selects the interpreter family (`cmd.exe` vs PowerShell),\n/// and the interpreter is located at spawn time. The only fail-fast condition\n/// worth catching up front is an empty/whitespace value, which would otherwise\n/// spawn with no program.\n#[cfg(windows)]\nfn validate_shell_windows(shell: &str) -> anyhow::Result<()> {\n    if shell.trim().is_empty() {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/platform/mod.rs#L69-L105","documentation":"The final validate_shell check on unix: the resolved file must have at least one execute bit set (mode & 0o111). The source comment is explicit that this is deliberately coarse — it rejects 'nobody can execute' rather than predicting whether the current user can; the kernel's spawn remains the real authority on ACLs and caps.","triggerScenarios":"runtime.shell resolves to a file with mode 644 (rw-r--r--) — a copied binary or wrapper script that lost its exec bit — or any file where no execute bit is set for user, group, or other.","commonSituations":"Copying a shell binary or wrapper script without preserving modes; extracting archives that drop permissions; wrapper scripts created by echo instead of install -m +x; cloud-synced files restored without their exec bit.","solutions":["chmod +x <shell-path> and retry.","If the value is your own wrapper, restore its exec bit in whatever provisions it (install -m 755).","If the file sits on a noexec mount, move it (or remount with exec) — the bit alone will not make spawn succeed.","Or point runtime.shell at a system shell that is already executable."],"exampleFix":"# before: -rw-r--r-- /opt/wrappers/sh\n$ zeroclaw ...  # error: not executable\n\n# after\n$ chmod +x /opt/wrappers/sh","handlingStrategy":"validation","validationCode":"#[cfg(unix)]\nuse std::os::unix::fs::PermissionsExt;\n\nfn has_exec_bit(p: &std::path::Path) -> bool {\n    std::fs::metadata(p)\n        .map(|m| m.permissions().mode() & 0o111 != 0)\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match create_runtime(&config) {\n    Err(e) if e.to_string().contains(\"is not executable\") => {\n        // chmod +x the resolved path (watch for noexec mounts) and retry\n    }\n    other => other,\n}","preventionTips":["Provision wrapper scripts with install -m 755, not by writing a file.","Preserve modes when copying shells (cp -p) and extracting archives.","Remember the check is coarse: an exec bit on a noexec mount still fails at spawn."],"tags":["runtime","shell","permissions","executable-bit"],"backgroundTag":"file-not-executable","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}