astrid-runtime/astrid · error
{primary:#}; additional gateway cleanup failure: {secondary:
Error message
{primary:#}; additional gateway cleanup failure: {secondary:#} What it means
combine_gateway_results merges the accept-loop result and the gateway cleanup result. If BOTH fail, the error is neither suppressed nor lost: the primary error is re-raised with the secondary cleanup error appended as context, so a cleanup failure cannot mask the real accept failure (or vice versa).
Source
Thrown at crates/astrid-cli/src/commands/mcp/gateway.rs:557
state.wait_for_stop_acks(),
)
.await
.context("shutdown stage gateway.final_ack_delivery")?;
}
let result = combine_gateway_results(accept_result, cleanup_result);
if result.is_ok() && !control_stop {
crate::commands::daemon::retire_disconnected_projection(daemon_pid).await?;
}
result
}
fn combine_gateway_results(accept: Result<ExitCode>, cleanup: Result<()>) -> Result<ExitCode> {
match (accept, cleanup) {
(Ok(exit), Ok(())) => Ok(exit),
(Err(primary), Ok(())) | (Ok(_), Err(primary)) => Err(primary),
(Err(primary), Err(secondary)) => {
anyhow::bail!("{primary:#}; additional gateway cleanup failure: {secondary:#}")
},
}
}
async fn accept_loop(
listener: UnixListener,
state: Arc<GatewayState>,
idle_grace: Duration,
) -> Result<ExitCode> {
let idle = tokio::time::sleep(idle_grace);
tokio::pin!(idle);
loop {
tokio::select! {
biased;
() = state.shutdown.cancelled() => return Ok(ExitCode::SUCCESS),
// Retain and consume the final disconnect before an expired timer.
() = state.connections_drained.notified() => {
idle.as_mut().reset(Instant::now().checked_add(idle_grace)View on GitHub (pinned to affd8760f4)
Solutions
- Fix the primary accept-loop error first — the appended message after the ';' is secondary cleanup noise.
- If the trailing 'additional gateway cleanup failure' mentions ready/lease/socket files, remove stale files under the daemon_root directory manually and check permissions.
- Re-run the gateway after correcting the root cause; this composite error is diagnostic, not a configuration knob.
- If cleanup failures recur, verify the daemon_root directory is writable by the gateway's user.
Example fix
// diagnosis: read the part before ';' as the real failure // 'uplink connect failed: ...; additional gateway cleanup failure: permission denied removing ready file' // after: fix permissions on daemon_root so cleanup succeeds chmod u+rwX ~/.astrid/daemon
Defensive patterns
Strategy: try-catch
Try / catch
match run_gateway().await {
Err(e) if e.to_string().contains("additional gateway cleanup failure") => {
let (primary, _secondary) = e.to_string().split_once("; additional").unwrap();
report_root_cause(primary);
},
other => other?,
} Prevention
- Ensure daemon_root is writable by the gateway user so cleanup never fails.
- Remove stale ready/lease/socket files after abnormal exits.
- Fix accept-loop root causes promptly; the appended cleanup text is secondary.
- Check filesystem permissions if cleanup failures recur.
When it happens
Trigger: The accept loop exits with an error (Err(primary)) while gateway cleanup also returns Err(secondary); returned from run() and exercised by gateway_cleanup_failure_does_not_mask_accept_failure.
Common situations: A fatal accept-loop error (e.g. uplink/socket failure) coincides with cleanup failures like being unable to remove the gateway ready/lease files under daemon_root or socket cleanup failing due to permissions.
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
- MCP gateway attach limit reached for principal '{principal}'
- expected MCP attach registration
- MCP attach registration is missing or too large
- unsupported MCP gateway control version {}
- MCP gateway control authority is incomplete
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/10b573021e26dfdc.
Report an issue: GitHub.