{"record":{"id":"ede6d7aa915e2b53","repo":"astrid-runtime/astrid","slug":"registry-did-not-respond","errorCode":null,"errorMessage":"registry did not respond","messagePattern":"registry did not respond","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/models.rs","lineNumber":269,"sourceCode":"    // foreign `corr_id` (another concurrent same-principal SET's reply). The\n    // budget is an absolute DEADLINE, not a per-iteration timeout: each\n    // `recv` is bounded by the time REMAINING, so a stream of skipped foreign\n    // replies can never extend the total wait past the original budget.\n    let timeout = state.registry_timeout.unwrap_or(REGISTRY_TIMEOUT);\n    // `checked_add` over the bare `+` so an absurd timeout can't panic on\n    // overflow; saturating to `now` (a zero remaining budget) on overflow is\n    // a harmless immediate timeout that the production budget never hits.\n    let deadline = tokio::time::Instant::now()\n        .checked_add(timeout)\n        .unwrap_or_else(tokio::time::Instant::now);\n    loop {\n        let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());\n        // No budget left (deadline already elapsed, or zero remaining): break\n        // to the timeout path rather than calling `recv(Some(ZERO))`, whose\n        // behaviour with a zero duration is implementation-defined. The\n        // absolute deadline above keeps total wait bounded regardless.\n        if remaining.is_zero() {\n            return Err(GatewayError::Internal(anyhow::anyhow!(\n                \"registry did not respond\"\n            )));\n        }\n        let Some(event) = reply_rx.recv(Some(remaining)).await else {\n            return Err(GatewayError::Internal(anyhow::anyhow!(\n                \"registry did not respond\"\n            )));\n        };\n        let AstridEvent::Ipc { message, .. } = &*event else {\n            return Err(GatewayError::Internal(anyhow::anyhow!(\n                \"registry reply was not an IPC message\"\n            )));\n        };\n        if !expected_source_ids.contains(&message.source_id) {\n            continue;\n        }\n        // Extract the guest-facing payload. Capsule `publish_json` arrives as\n        // `Custom { data }` when the JSON has no known IPC `type`; using the","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/models.rs#L251-L287","documentation":"Inside `registry_round_trip`'s bounded wait loop, if the absolute deadline has already elapsed (or has zero remaining budget) before awaiting the reply, the function short-circuits with `GatewayError::Internal('registry did not respond')` instead of calling `recv(Some(ZERO))`. It means the caller's time budget for the registry reply ran out before any reply was received.","triggerScenarios":"The registry reply did not arrive within the configured deadline, and by the time the loop re-checked, `deadline.saturating_duration_since(now)` was zero — typically after one or more unrelated/late events were consumed from `reply_rx` or the registry was simply slow.","commonSituations":"Registry capsule slow to load or busy; high gateway load starving the reply channel; too-short request timeout configured; wrong request topic so no subscriber ever answers and the deadline expires.","solutions":["Increase the registry round-trip deadline/timeout if it is tighter than realistic registry latency.","Verify the registry capsule is loaded and subscribed to the request topic (see the related 'no loaded capsule handles the registry request' error).","Check registry load/health; a slow or wedged capsule delays replies past the deadline.","Confirm request/response topic names and correlation IDs match so replies are not discarded as unexpected."],"exampleFix":"// before\nif remaining.is_zero() {\n    return Err(GatewayError::Internal(anyhow::anyhow!(\"registry did not respond\")));\n}\n// after\nif remaining.is_zero() {\n    tracing::warn!(%request_topic, \"registry reply deadline elapsed\");\n    return Err(GatewayError::Timeout(\"registry did not respond within deadline\".into()));\n}","handlingStrategy":"retry","validationCode":"let deadline_budget = registry_deadline_config();\nif deadline_budget < MIN_REGISTRY_DEADLINE {\n    return Err(configError(\"registry deadline too small\"));\n}","typeGuard":null,"tryCatchPattern":"match list_models(&state, caller).await {\n    Err(e) if e.message().contains(\"registry did not respond\") => {\n        tokio::time::sleep(BACKOFF).await;\n        list_models(&state, caller).await // one retry before surfacing\n    }\n    r => r,\n}","preventionTips":["Set the registry deadline above worst-case observed capsule latency.","Retry once with backoff for transient slowness before failing the request.","Monitor round-trip latency percentiles to right-size the deadline.","Verify capsule subscription readiness before sending the request."],"tags":["timeout","registry","ipc","gateway"],"backgroundTag":"request-timeout","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}