{"record":{"id":"10b573021e26dfdc","repo":"astrid-runtime/astrid","slug":"primary-additional-gateway-cleanup-failure","errorCode":null,"errorMessage":"{primary:#}; additional gateway cleanup failure: {secondary:#}","messagePattern":"(.+?); additional gateway cleanup failure: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/gateway.rs","lineNumber":557,"sourceCode":"            state.wait_for_stop_acks(),\n        )\n        .await\n        .context(\"shutdown stage gateway.final_ack_delivery\")?;\n    }\n\n    let result = combine_gateway_results(accept_result, cleanup_result);\n    if result.is_ok() && !control_stop {\n        crate::commands::daemon::retire_disconnected_projection(daemon_pid).await?;\n    }\n    result\n}\n\nfn combine_gateway_results(accept: Result<ExitCode>, cleanup: Result<()>) -> Result<ExitCode> {\n    match (accept, cleanup) {\n        (Ok(exit), Ok(())) => Ok(exit),\n        (Err(primary), Ok(())) | (Ok(_), Err(primary)) => Err(primary),\n        (Err(primary), Err(secondary)) => {\n            anyhow::bail!(\"{primary:#}; additional gateway cleanup failure: {secondary:#}\")\n        },\n    }\n}\n\nasync fn accept_loop(\n    listener: UnixListener,\n    state: Arc<GatewayState>,\n    idle_grace: Duration,\n) -> Result<ExitCode> {\n    let idle = tokio::time::sleep(idle_grace);\n    tokio::pin!(idle);\n    loop {\n        tokio::select! {\n            biased;\n            () = state.shutdown.cancelled() => return Ok(ExitCode::SUCCESS),\n            // Retain and consume the final disconnect before an expired timer.\n            () = state.connections_drained.notified() => {\n                idle.as_mut().reset(Instant::now().checked_add(idle_grace)","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/gateway.rs#L539-L575","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// diagnosis: read the part before ';' as the real failure\n// 'uplink connect failed: ...; additional gateway cleanup failure: permission denied removing ready file'\n// after: fix permissions on daemon_root so cleanup succeeds\nchmod u+rwX ~/.astrid/daemon","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match run_gateway().await {\n    Err(e) if e.to_string().contains(\"additional gateway cleanup failure\") => {\n        let (primary, _secondary) = e.to_string().split_once(\"; additional\").unwrap();\n        report_root_cause(primary);\n    },\n    other => other?,\n}","preventionTips":["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."],"tags":["mcp","gateway","error-handling","cleanup"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}