{"record":{"id":"9487ef7d954fbefb","repo":"astrid-runtime/astrid","slug":"mcp-gateway-lock-path-has-no-parent","errorCode":null,"errorMessage":"MCP gateway lock path has no parent","messagePattern":"MCP gateway lock path has no parent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/lifecycle.rs","lineNumber":99,"sourceCode":"#[derive(Debug)]\npub(crate) struct GatewaySupervisorLock(std::fs::File);\n\nimpl GatewayLifecycleLock {\n    pub(crate) const fn file(&self) -> &std::fs::File {\n        &self.0\n    }\n}\n\nimpl GatewaySupervisorLock {\n    pub(crate) const fn file(&self) -> &std::fs::File {\n        &self.0\n    }\n}\n\nfn open_lock_file(path: &Path) -> Result<std::fs::File> {\n    let parent = path\n        .parent()\n        .ok_or_else(|| anyhow::anyhow!(\"MCP gateway lock path has no parent\"))?;\n    ensure_private_dir(parent)?;\n    let mut options = std::fs::OpenOptions::new();\n    options.read(true).write(true).create(true).truncate(false);\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::OpenOptionsExt as _;\n        options.mode(0o600);\n    }\n    options\n        .open(path)\n        .with_context(|| format!(\"failed to open {}\", path.display()))\n}\n\nfn try_lock_file(file: std::fs::File, path: &Path) -> Result<Option<std::fs::File>> {\n    match file.try_lock() {\n        Ok(()) => Ok(Some(file)),\n        Err(std::fs::TryLockError::WouldBlock) => Ok(None),\n        Err(std::fs::TryLockError::Error(error)) => {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/lifecycle.rs#L81-L117","documentation":"`open_lock_file` computes the parent directory of the lock-file path to create it with private (0600/0700) permissions. `Path::parent()` returns `None` only for paths with no directory component (e.g. a bare root). This error is an internal invariant guard: the gateway lock path is always derived from the runtime home, so hitting it means the path construction was corrupted.","triggerScenarios":"Essentially only when the lock path is misconstructed to a bare filename or filesystem root, e.g. a runtime home directory configured to `/` or empty, causing `gateway lock path` to resolve without a parent directory.","commonSituations":"A misconfigured environment/runtime home pointing at `/`; a bug or unusual override producing a root-relative lock path. Practically unreachable with normal installation layouts.","solutions":["Check the runtime home / state directory configuration; it must be a normal directory path, not `/` or empty.","Report as a bug if the path was constructed by the library itself with default configuration.","Run with default settings (no home/path overrides) to confirm the error disappears."],"exampleFix":"// before\nASTRID_RUNTIME_HOME=/ astrid mcp gateway run\n// after\nASTRID_RUNTIME_HOME=$HOME/.local/share/astrid astrid mcp gateway run","handlingStrategy":"validation","validationCode":"let home = std::env::var(\"ASTRID_RUNTIME_HOME\").unwrap_or_default();\nassert!(home != \"/\" && !home.is_empty(), \"runtime home must be a normal directory\");","typeGuard":"fn has_parent(p: &std::path::Path) -> bool { p.parent().is_some() }","tryCatchPattern":"if let Err(e) = open_lock_file(&path) {\n    if e.to_string().contains(\"has no parent\") {\n        eprintln!(\"misconfigured lock path: {}\", path.display());\n    }\n    return Err(e);\n}","preventionTips":["Never point the runtime home at `/` or an empty value","Let the library derive lifecycle paths from its default home","Validate path overrides at startup","Report occurrences with default config as bugs"],"tags":["mcp","lifecycle","path","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}