{"record":{"id":"1cda3bd9c11bdc5c","repo":"astrid-runtime/astrid","slug":"invalid-mcp-gateway-startup-lease-at","errorCode":null,"errorMessage":"invalid MCP gateway startup lease at {}","messagePattern":"invalid MCP gateway startup lease at (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/lifecycle.rs","lineNumber":170,"sourceCode":"\npub(crate) fn read_gateway_startup_lease() -> Result<Option<GatewayStartupLease>> {\n    let path = gateway_startup_lease_path()?;\n    match std::fs::read(&path) {\n        Ok(bytes) => {\n            let lease: GatewayStartupLease = serde_json::from_slice(&bytes).with_context(|| {\n                format!(\"invalid MCP gateway startup lease at {}\", path.display())\n            })?;\n            if lease.version != 1\n                || lease.principal.is_empty()\n                || lease.boot_token.len() != 32\n                || lease.supervisor_pid == 0\n                || lease.gateway_pid.is_some_and(|pid| pid == 0)\n                || lease\n                    .gateway_exe\n                    .as_ref()\n                    .is_none_or(|path| path.as_os_str().is_empty())\n            {\n                anyhow::bail!(\"invalid MCP gateway startup lease at {}\", path.display());\n            }\n            Ok(Some(lease))\n        },\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),\n        Err(error) => Err(error).with_context(|| format!(\"failed to read {}\", path.display())),\n    }\n}\n\npub(crate) fn write_gateway_startup_lease(lease: &GatewayStartupLease) -> Result<()> {\n    let path = gateway_startup_lease_path()?;\n    let parent = path\n        .parent()\n        .ok_or_else(|| anyhow::anyhow!(\"MCP gateway startup lease path has no parent\"))?;\n    ensure_private_dir(parent)?;\n    let temp = path.with_extension(format!(\"starting.tmp.{}\", std::process::id()));\n    let bytes = serde_json::to_vec(lease).context(\"failed to encode MCP gateway startup lease\")?;\n    std::fs::write(&temp, bytes).with_context(|| format!(\"failed to write {}\", temp.display()))?;\n    #[cfg(unix)]","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/lifecycle.rs#L152-L188","documentation":"read_gateway_startup_lease parses the gateway's startup lease file and sanity-checks its contents. A lease is considered invalid when the gateway PID is 0 or the recorded gateway executable path is empty/missing — meaning the file exists but cannot describe a live, locatable gateway. The corrupt/invalid lease is treated as an error rather than silently ignored so callers can clean up deterministically.","triggerScenarios":"wait_for_gateway/stop_gateway/stop_startup_gateway read a lease file whose gateway_pid is 0, gateway_exe is empty, or equivalent malformed fields; partially written or truncated lease file.","commonSituations":"A previous gateway crashed mid-write leaving a truncated lease; lease file edited or corrupted on disk; version mismatch where an older gateway wrote a lease shape a newer reader rejects; leftover lease from an unclean machine shutdown.","solutions":["Delete the invalid startup lease file and restart the gateway so a fresh lease is written","Verify no gateway process is running (the stale PID may be dead) then remove leftover lease files under the runtime dir","Upgrade/downgrade so reader and writer agree on the lease format","Check disk health if truncation recurs"],"exampleFix":"// before\n// assuming lease file is valid\nlet lease = read_gateway_startup_lease()?;\n// after\nmatch read_gateway_startup_lease() {\n    Ok(lease) => lease,\n    Err(_) => { let _ = std::fs::remove_file(gateway_startup_lease_path()?); None }\n}","handlingStrategy":"try-catch","validationCode":"if let Ok(bytes) = std::fs::read(&lease_path) {\n    if let Ok(lease) = serde_json::from_slice::<GatewayStartupLease>(&bytes) {\n        if lease.gateway_pid == Some(0) || lease.gateway_exe.as_ref().is_none_or(|p| p.as_os_str().is_empty()) {\n            let _ = std::fs::remove_file(&lease_path); // clean invalid lease first\n        }\n    }\n}","typeGuard":"fn lease_is_valid(l: &GatewayStartupLease) -> bool {\n    !l.gateway_pid.is_some_and(|pid| pid == 0)\n        && l.gateway_exe.as_ref().is_some_and(|p| !p.as_os_str().is_empty())\n}","tryCatchPattern":"match read_gateway_startup_lease() {\n    Ok(lease) => use(lease),\n    Err(e) if e.to_string().contains(\"invalid MCP gateway startup lease\") => {\n        let _ = std::fs::remove_file(gateway_startup_lease_path()?);\n        restart_gateway()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Write lease files atomically (write temp + rename) to avoid truncation","Clean stale leases on unclean-shutdown detection","Keep lease format versioned"],"tags":["lifecycle","mcp","corrupt-state"],"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-14T05:17:10.506Z"}