astrid-runtime/astrid · error

MCP gateway startup lease path has no parent

Error message

MCP gateway startup lease path has no parent

What it means

`write_gateway_startup_lease` writes the startup lease (identity of a gateway holding the lifecycle lock but not yet ready) atomically via a temp file next to the lease path. Before writing, it requires the lease path to have a parent directory; `Path::parent()` returning `None` triggers this error. Like the other "has no parent" errors, it is an invariant guard against a degenerate path.

Source

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

                || 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)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::fs::set_permissions(&temp, std::fs::Permissions::from_mode(0o600))?;
    }
    std::fs::rename(&temp, &path).with_context(|| format!("failed to publish {}", path.display()))
}

pub(crate) fn remove_gateway_startup_lease(boot_token: Option<&str>) -> Result<()> {
    let path = gateway_startup_lease_path()?;
    let lease = read_gateway_startup_lease()?;
    if let Some(lease) = lease
        && let Some(expected) = boot_token
        && lease.boot_token != expected

View on GitHub (pinned to affd8760f4)

Solutions

  1. Fix the runtime home / state directory configuration so the lease path has a real parent directory.
  2. Retry with default configuration to rule out an override causing the degenerate path.
  3. Report as a bug if it occurs with stock settings.

Example fix

// before
ASTRID_RUNTIME_HOME=/ astrid mcp gateway run
// after
unset ASTRID_RUNTIME_HOME && astrid mcp gateway run
Defensive patterns

Strategy: validation

Validate before calling

let home = std::env::var("ASTRID_RUNTIME_HOME").unwrap_or_default();
assert!(home != "/" && !home.is_empty(), "runtime home must be a normal directory");

Type guard

fn has_parent(p: &std::path::Path) -> bool { p.parent().is_some() }

Try / catch

if let Err(e) = write_gateway_startup_lease(&lease) {
    if e.to_string().contains("has no parent") {
        eprintln!("bad lease path configuration");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: The startup lease path (`mcp-gateway.starting`) resolves to a path with no directory component, which only happens if the runtime home / state root is misconfigured to `/` or a bare root, or if path construction is broken.

Common situations: Runtime home misconfigured to the filesystem root; a library bug in lease-path derivation. Not expected in normal operation.

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/82e673a37f8799e4. Report an issue: GitHub.