astrid-runtime/astrid · error

invalid MCP gateway startup lease at {}

Error message

invalid MCP gateway startup lease at {}

What it means

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.

Source

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

pub(crate) fn read_gateway_startup_lease() -> Result<Option<GatewayStartupLease>> {
    let path = gateway_startup_lease_path()?;
    match std::fs::read(&path) {
        Ok(bytes) => {
            let lease: GatewayStartupLease = serde_json::from_slice(&bytes).with_context(|| {
                format!("invalid MCP gateway startup lease at {}", path.display())
            })?;
            if lease.version != 1
                || lease.principal.is_empty()
                || lease.boot_token.len() != 32
                || lease.supervisor_pid == 0
                || lease.gateway_pid.is_some_and(|pid| pid == 0)
                || lease
                    .gateway_exe
                    .as_ref()
                    .is_none_or(|path| path.as_os_str().is_empty())
            {
                anyhow::bail!("invalid MCP gateway startup lease at {}", path.display());
            }
            Ok(Some(lease))
        },
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
        Err(error) => Err(error).with_context(|| format!("failed to read {}", path.display())),
    }
}

pub(crate) fn write_gateway_startup_lease(lease: &GatewayStartupLease) -> Result<()> {
    let path = gateway_startup_lease_path()?;
    let parent = path
        .parent()
        .ok_or_else(|| anyhow::anyhow!("MCP gateway startup lease path has no parent"))?;
    ensure_private_dir(parent)?;
    let temp = path.with_extension(format!("starting.tmp.{}", std::process::id()));
    let bytes = serde_json::to_vec(lease).context("failed to encode MCP gateway startup lease")?;
    std::fs::write(&temp, bytes).with_context(|| format!("failed to write {}", temp.display()))?;
    #[cfg(unix)]

View on GitHub (pinned to affd8760f4)

Solutions

  1. Delete the invalid startup lease file and restart the gateway so a fresh lease is written
  2. Verify no gateway process is running (the stale PID may be dead) then remove leftover lease files under the runtime dir
  3. Upgrade/downgrade so reader and writer agree on the lease format
  4. Check disk health if truncation recurs

Example fix

// before
// assuming lease file is valid
let lease = read_gateway_startup_lease()?;
// after
match read_gateway_startup_lease() {
    Ok(lease) => lease,
    Err(_) => { let _ = std::fs::remove_file(gateway_startup_lease_path()?); None }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if let Ok(bytes) = std::fs::read(&lease_path) {
    if let Ok(lease) = serde_json::from_slice::<GatewayStartupLease>(&bytes) {
        if lease.gateway_pid == Some(0) || lease.gateway_exe.as_ref().is_none_or(|p| p.as_os_str().is_empty()) {
            let _ = std::fs::remove_file(&lease_path); // clean invalid lease first
        }
    }
}

Type guard

fn lease_is_valid(l: &GatewayStartupLease) -> bool {
    !l.gateway_pid.is_some_and(|pid| pid == 0)
        && l.gateway_exe.as_ref().is_some_and(|p| !p.as_os_str().is_empty())
}

Try / catch

match read_gateway_startup_lease() {
    Ok(lease) => use(lease),
    Err(e) if e.to_string().contains("invalid MCP gateway startup lease") => {
        let _ = std::fs::remove_file(gateway_startup_lease_path()?);
        restart_gateway()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


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