{"record":{"id":"3853685573433ab9","repo":"zeroclaw-labs/zeroclaw","slug":"runtime-shell-shell-resolved-to-is-not-a","errorCode":null,"errorMessage":"runtime.shell {shell:?} (resolved to {}) is not a regular file","messagePattern":"runtime\\.shell (.+?) \\(resolved to (.+?)\\) is not a regular file","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/platform/mod.rs","lineNumber":75,"sourceCode":"        }\n    };\n\n    if !resolved.exists() {\n        anyhow::bail!(\n            \"runtime.shell {shell:?} (resolved to {}) does not exist\",\n            resolved.display()\n        );\n    }\n\n    let metadata = match resolved.metadata() {\n        Ok(metadata) => metadata,\n        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(())","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/platform/mod.rs#L57-L93","documentation":"validate_shell requires the resolved shell path to be a regular file. Directories, fifos, sockets, and device nodes all parse as paths but cannot be exec'd as a program, so they are rejected before spawn.","triggerScenarios":"runtime.shell resolving to a directory (\"/usr/bin\" instead of \"/usr/bin/bash\"), a symlink whose target is a fifo or socket, or a device node.","commonSituations":"Typos dropping the binary name and leaving the trailing slash or bare directory; tool-managed symlinks that replaced the binary with something odd; minimal container images with an unusual /bin.","solutions":["Point at the shell binary itself, not its directory.","Inspect what the path really is: ls -l and file <path>; fix or remove symlinks that target a non-file.","If the shell package's files are corrupted, reinstall it."],"exampleFix":"# before\n[runtime]\nshell = \"/usr/bin\"   # a directory\n\n# after\n[runtime]\nshell = \"/usr/bin/bash\"","handlingStrategy":"type-guard","validationCode":"if !is_regular_file(&resolved_shell_path) {\n    return Err(anyhow::anyhow!(\"shell path is not a regular file\"));\n}","typeGuard":"fn is_regular_file(p: &std::path::Path) -> bool {\n    std::fs::metadata(p).map(|m| m.is_file()).unwrap_or(false)\n}","tryCatchPattern":"match create_runtime(&config) {\n    Err(e) if e.to_string().contains(\"not a regular file\") => {\n        // path points at a dir/fifo/socket; point at the binary itself\n    }\n    other => other,\n}","preventionTips":["Point at the shell binary, never its directory.","Check ls -l / file on unusual paths (tool-managed symlinks) before configuring them.","Lint configs for shell values ending in a path separator."],"tags":["runtime","shell","file-type"],"backgroundTag":"path-is-not-a-regular-file","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}