astrid-runtime/astrid · error

shutdown stage gateway.startup_stop: a starting gateway did…

Error message

shutdown stage gateway.startup_stop: a starting gateway did not become stoppable within {} seconds

What it means

Fired by stop_gateway's shutdown stage gateway.startup_stop when a gateway that is still starting does not reach a stoppable state within READY_TIMEOUT. The shutdown path gives the starting gateway a grace window, then gives up rather than waiting forever.

Solutions

  1. Force-kill the starting gateway/supervisor process
  2. Clean leftover startup lease and ready-record files, then restart
  3. Investigate why the gateway never became stoppable (logs, resource starvation)
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at crates/astrid-cli/src/commands/mcp/lifecycle.rs:539 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/ed35a19a4c5b7ec8. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/mcp/lifecycle.rs:539

    }
    let Some(record) = read_gateway_ready()? else {
        let deadline = Instant::now()
            .checked_add(READY_TIMEOUT)
            .unwrap_or_else(Instant::now);
        while try_acquire_gateway_lifecycle()?.is_none() {
            if let Some(record) = read_gateway_ready()? {
                return stop_ready_gateway(record, socket).await;
            }
            if let Some(lease) = read_gateway_startup_lease()?
                && lease
                    .gateway_pid
                    .is_some_and(crate::commands::daemon_control::is_process_alive)
            {
                stop_startup_gateway(&lease).await?;
                continue;
            }
            if Instant::now() >= deadline {
                anyhow::bail!(
                    "shutdown stage gateway.startup_stop: a starting gateway did not become stoppable within {} seconds",
                    READY_TIMEOUT.as_secs()
                );
            }
            tokio::time::sleep(READY_POLL).await;
        }
        let lifecycle = try_acquire_gateway_lifecycle()?.ok_or_else(|| {
            anyhow::anyhow!("shutdown stage gateway.startup_stop: lifecycle changed")
        })?;
        clean_unowned_gateway_startup(&lifecycle)
            .await
            .context("shutdown stage gateway.stale_listener_cleanup")?;
        drop(lifecycle);
        return Ok(());
    };
    stop_ready_gateway(record, socket).await
}

View on GitHub (pinned to affd8760f4)