{"record":{"id":"5ff0bb7a25764d98","repo":"astrid-runtime/astrid","slug":"shutdown-stage-gateway-startup-identity-lease-dis","errorCode":null,"errorMessage":"shutdown stage gateway.startup_identity: lease disappeared","messagePattern":"shutdown stage gateway\\.startup_identity: lease disappeared","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/lifecycle.rs","lineNumber":605,"sourceCode":"    .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        );\n    }\n    Ok(())\n}","sourceCodeStart":587,"sourceCodeEnd":623,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/lifecycle.rs#L587-L623","documentation":"`stop_startup_gateway` shuts down a gateway that is still starting. It reads the on-disk startup lease a second time and compares it to the lease observed earlier; this error is thrown when the lease file has disappeared, meaning the startup generation ended (crashed, completed and replaced by the ready record, or cleaned up by another process) between the two reads.","triggerScenarios":"The startup lease file (`mcp-gateway.starting`) is deleted between `stop_gateway` reading it and `stop_startup_gateway` re-reading it — e.g. the starting gateway crashed or finished starting (lease removed/converted to ready) concurrently, or a stale-startup cleanup ran in between.","commonSituations":"Stopping the gateway at the exact moment its startup completes or fails; a concurrent `stop` or cleanup process removing the lease; the starting gateway process dying mid-startup.","solutions":["Re-run the stop command; the transient startup generation has ended, so re-evaluating state will follow the normal stop path.","Check the ready file/socket: if the gateway finished starting, it will be stopped via the normal (non-startup) path on retry.","Avoid racing multiple stop commands; run one shutdown at a time.","Inspect gateway logs to confirm the starting process crashed or completed during shutdown."],"exampleFix":"// before\nastrid mcp gateway stop   # single attempt may hit transient lease removal\n// after\nastrid mcp gateway stop || astrid mcp gateway stop","handlingStrategy":"retry","validationCode":"// Check current startup state before stopping\nlet leasing = read_gateway_startup_lease()?;\neprintln!(\"startup lease present: {}\", leasing.is_some());","typeGuard":null,"tryCatchPattern":"if let Err(e) = stop_gateway().await {\n    if e.to_string().contains(\"lease disappeared\") {\n        eprintln!(\"startup generation ended mid-stop; retrying\");\n        stop_gateway().await?;\n    } else { return Err(e); }\n}","preventionTips":["Avoid stopping exactly while startup is in flight; wait for ready","Run a single stop command at a time","Monitor gateway logs to know when startup completed or crashed","Retry transient lifecycle errors once before escalating"],"tags":["mcp","lifecycle","race-condition","startup"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}