astrid-runtime/astrid · error
shutdown stage gateway.startup_identity: lease has no execut
Error message
shutdown stage gateway.startup_identity: lease has no executable
What it means
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.
Source
Thrown at crates/astrid-cli/src/commands/mcp/lifecycle.rs:602
record.pid,
crate::commands::daemon_control::GRACE,
)
.await
{
anyhow::bail!(
"shutdown stage gateway.process_reap: authenticated gateway PID {} did not exit",
record.pid
);
}
remove_dead_gateway_markers(&record).await
}
async fn stop_startup_gateway(lease: &GatewayStartupLease) -> Result<()> {
let Some(gateway_pid) = lease.gateway_pid else {
anyhow::bail!("shutdown stage gateway.startup_identity: lease has no gateway PID");
};
let Some(gateway_exe) = lease.gateway_exe.as_deref() else {
anyhow::bail!("shutdown stage gateway.startup_identity: lease has no executable");
};
let current = read_gateway_startup_lease()?.ok_or_else(|| {
anyhow::anyhow!("shutdown stage gateway.startup_identity: lease disappeared")
})?;
if current != *lease {
anyhow::bail!("shutdown stage gateway.startup_identity: startup generation changed");
}
let outcome =
crate::commands::daemon_control::terminate_known(gateway_pid, Some(gateway_exe)).await;
if !matches!(
outcome,
crate::commands::daemon_control::KillOutcome::TermExited
| crate::commands::daemon_control::KillOutcome::KilledExited
) {
anyhow::bail!(
"shutdown stage gateway.startup_reap: starting gateway PID {gateway_pid} is {outcome:?}"
);View on GitHub (pinned to affd8760f4)
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
Example fix
// before
let gateway_exe = lease.gateway_exe.as_deref().unwrap();
// after: ensure a full lease exists before shutdown
if lease.gateway_exe.is_none() {
remove_gateway_startup_lease(None)?;
anyhow::bail!("stale lease; restart gateway to regenerate");
} Defensive patterns
Strategy: validation
Validate before calling
let lease = read_gateway_startup_lease()?;
anyhow::ensure!(
lease.as_ref().and_then(|l| l.gateway_exe.as_deref()).is_some(),
"startup lease missing executable; restart gateway"
); Type guard
fn has_gateway_exe(l: &GatewayStartupLease) -> bool {
l.gateway_exe.as_deref().is_some_and(|p| !p.is_empty())
} Try / catch
match stop_gateway().await {
Err(e) if e.to_string().contains("lease has no executable") => {
remove_gateway_startup_lease(None)?; // regenerate via next startup
},
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- shutdown stage gateway.startup_identity: lease has no gatewa
- shutdown stage gateway.startup_reap: starting gateway PID {g
- {primary:#}; additional shutdown failure: {secondary:#}
- shutdown stage daemon.shutdown_ack: rejected: {reason}
- shutdown stage daemon.shutdown_ack: unexpected response: {ot
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/28bd982e4503f84b.
Report an issue: GitHub.