{"record":{"id":"28bd982e4503f84b","repo":"astrid-runtime/astrid","slug":"shutdown-stage-gateway-startup-identity-lease-has-28bd98","errorCode":null,"errorMessage":"shutdown stage gateway.startup_identity: lease has no executable","messagePattern":"shutdown stage gateway\\.startup_identity: lease has no executable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/lifecycle.rs","lineNumber":602,"sourceCode":"        record.pid,\n        crate::commands::daemon_control::GRACE,\n    )\n    .await\n    {\n        anyhow::bail!(\n            \"shutdown stage gateway.process_reap: authenticated gateway PID {} did not exit\",\n            record.pid\n        );\n    }\n    remove_dead_gateway_markers(&record).await\n}\n\nasync fn stop_startup_gateway(lease: &GatewayStartupLease) -> Result<()> {\n    let Some(gateway_pid) = lease.gateway_pid else {\n        anyhow::bail!(\"shutdown stage gateway.startup_identity: lease has no gateway PID\");\n    };\n    let Some(gateway_exe) = lease.gateway_exe.as_deref() else {\n        anyhow::bail!(\"shutdown stage gateway.startup_identity: lease has no executable\");\n    };\n    let current = read_gateway_startup_lease()?.ok_or_else(|| {\n        anyhow::anyhow!(\"shutdown stage gateway.startup_identity: lease disappeared\")\n    })?;\n    if current != *lease {\n        anyhow::bail!(\"shutdown stage gateway.startup_identity: startup generation changed\");\n    }\n\n    let outcome =\n        crate::commands::daemon_control::terminate_known(gateway_pid, Some(gateway_exe)).await;\n    if !matches!(\n        outcome,\n        crate::commands::daemon_control::KillOutcome::TermExited\n            | crate::commands::daemon_control::KillOutcome::KilledExited\n    ) {\n        anyhow::bail!(\n            \"shutdown stage gateway.startup_reap: starting gateway PID {gateway_pid} is {outcome:?}\"\n        );","sourceCodeStart":584,"sourceCodeEnd":620,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/lifecycle.rs#L584-L620","documentation":"Thrown by stop_startup_gateway when the lease has no gateway_exe. The code needs the executable path alongside the PID to safely terminate the exact process it started (PID+exe identity check) and refuses to kill without it.","triggerScenarios":"stop_gateway called with a GatewayStartupLease whose gateway_exe is None — corrupt/truncated lease file, older lease format, or a lease deserialized from incomplete state.","commonSituations":"Binary was moved/renamed between start and stop so lease regeneration skipped the exe; state directory written by an older version; manual tampering with lease contents.","solutions":["Remove the incomplete lease file and let the next gateway startup write a complete one","Restart the gateway cleanly (start then stop) so the lease records both PID and executable","Verify no mixed-version state: clear state from older astrid installations","Report a bug if startup consistently produces leases without gateway_exe"],"exampleFix":"// before\nlet gateway_exe = lease.gateway_exe.as_deref().unwrap();\n// after: ensure a full lease exists before shutdown\nif lease.gateway_exe.is_none() {\n    remove_gateway_startup_lease(None)?;\n    anyhow::bail!(\"stale lease; restart gateway to regenerate\");\n}","handlingStrategy":"validation","validationCode":"let lease = read_gateway_startup_lease()?;\nanyhow::ensure!(\n    lease.as_ref().and_then(|l| l.gateway_exe.as_deref()).is_some(),\n    \"startup lease missing executable; restart gateway\"\n);","typeGuard":"fn has_gateway_exe(l: &GatewayStartupLease) -> bool {\n    l.gateway_exe.as_deref().is_some_and(|p| !p.is_empty())\n}","tryCatchPattern":"match stop_gateway().await {\n    Err(e) if e.to_string().contains(\"lease has no executable\") => {\n        remove_gateway_startup_lease(None)?; // regenerate via next startup\n    },\n    other => other?,\n}","preventionTips":["Don't move or rename the gateway binary between start and stop","Regenerate the lease after binary upgrades","Keep lease files owned and written only by the CLI"],"tags":["process-management","state-corruption","shutdown"],"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"}