{"record":{"id":"f721b208f7134489","repo":"astrid-runtime/astrid","slug":"projection-name-response-timed-out-after-30s","errorCode":null,"errorMessage":"projection-name response timed out after 30s","messagePattern":"projection-name response timed out after 30s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/doctor.rs","lineNumber":291,"sourceCode":"        )),\n    }\n}\n\nasync fn projection_name_diagnostic(\n    policy: ProjectionNamePolicyPreset,\n) -> Result<ProjectionNameDiagnostic> {\n    let mut client = tokio::time::timeout(\n        Duration::from_secs(5),\n        crate::socket_client::connect_kernel_for_workspace(None),\n    )\n    .await\n    .map_err(|_| anyhow::anyhow!(\"connection timed out after 5s\"))??;\n    tokio::time::timeout(\n        Duration::from_secs(30),\n        client.projection_name_diagnostic(policy),\n    )\n    .await\n    .map_err(|_| anyhow::anyhow!(\"projection-name response timed out after 30s\"))?\n}\n\n/// Render the FAIL detail line for a not-ready report: each missing piece,\n/// space-separated, with unsatisfied interfaces as `ns:iface (req)`.\nfn readiness_detail(report: &astrid_core::kernel_api::AgentLoopReadiness) -> String {\n    let mut parts: Vec<String> = Vec::new();\n    if report.prompt_subscribers.is_empty() {\n        parts.push(format!(\n            \"no capsule subscribes {}\",\n            astrid_capsule::readiness::AGENT_PROMPT_TOPIC\n        ));\n    }\n    if report.response_publishers.is_empty() {\n        parts.push(format!(\n            \"no capsule publishes {}\",\n            astrid_capsule::readiness::AGENT_RESPONSE_TOPIC\n        ));\n    }","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/doctor.rs#L273-L309","documentation":"The projection-name diagnostic request is given a generous 30-second timeout (larger than the 5s defaults elsewhere because the diagnostic can be expensive). If the daemon fails to respond within 30 seconds, this error is raised. The connection succeeded but the diagnostic computation did not finish in time.","triggerScenarios":"Calling client.projection_name_diagnostic(policy) against a daemon whose diagnostic pass (projection naming analysis) exceeds 30s — very large workspaces or a stalled daemon.","commonSituations":"Huge projection graphs, daemon busy with other requests, or a daemon bug causing the diagnostic to hang.","solutions":["Retry once the daemon is less loaded","Reduce workspace size or split the diagnostic into smaller queries","Profile the daemon's projection_name_diagnostic implementation for hangs","Raise the 30s timeout only as a last resort"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in 0..2 {\n    match projection_name_diagnostic(&policy).await {\n        Err(e) if e.to_string().contains(\"timed out after 30s\") && attempt == 0 => continue,\n        other => return other,\n    }\n}","preventionTips":["Run diagnostics on idle daemons","Keep projection graphs within tested size limits","Instrument the daemon diagnostic path to detect hangs","Only raise the 30s budget for provably large workspaces"],"tags":["timeout","daemon","ipc","diagnostics"],"backgroundTag":"request-timeout","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"}