{"record":{"id":"7dc555bf7a4369da","repo":"Hmbown/CodeWhale","slug":"ds4-v1-models-timed-out-after-15-seconds-at","errorCode":null,"errorMessage":"DS4 /v1/models timed out after 15 seconds at {}","messagePattern":"DS4 /v1/models timed out after 15 seconds at (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/tui/src/doctor.rs","lineNumber":584,"sourceCode":"\n/// Probe DS4 through its cheap `/v1/models` contract instead of waking the\n/// model for a completion. The selected model must be advertised.\npub(crate) async fn probe_ds4_models(config: &crate::config::Config) -> anyhow::Result<()> {\n    use crate::client::DeepSeekClient;\n    use crate::core::model_client::ModelClient;\n\n    let endpoint = crate::client::redact_url_for_display(&config.deepseek_base_url());\n    let client = DeepSeekClient::new(config)?;\n    let configured_alias = client.model().to_string();\n    let models = match tokio::time::timeout(\n        std::time::Duration::from_secs(15),\n        client.list_models(),\n    )\n    .await\n    {\n        Ok(Ok(models)) => models,\n        Ok(Err(error)) => anyhow::bail!(ds4_probe_error(config, &error.to_string())),\n        Err(_) => anyhow::bail!(\"DS4 /v1/models timed out after 15 seconds at {}\", endpoint),\n    };\n    if !models\n        .iter()\n        .any(|available| available.id == configured_alias)\n    {\n        let advertised = models\n            .iter()\n            .take(8)\n            .map(|available| available.id.as_str())\n            .collect::<Vec<_>>()\n            .join(\", \");\n        anyhow::bail!(\n            \"DS4 /v1/models at {} did not list configured alias '{configured_alias}' (advertised: {})\",\n            endpoint,\n            if advertised.is_empty() {\n                \"none\"\n            } else {\n                advertised.as_str()","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/doctor.rs#L566-L602","documentation":"Thrown by probe_ds4_models when the tokio::time::timeout wrapper around client.list_models() elapses after 15 seconds without a result. The endpoint (redacted) is included so you can tell which base URL hung.","triggerScenarios":"ds4-server accepting the TCP connection but never answering /v1/models within 15s: an overloaded or wedged server, a stalled proxy, or a blackholed route.","commonSituations":"ds4-server busy loading a model; slow reverse proxy; network path with high latency or packet loss; server deadlocked after a crash-loop.","solutions":["Check ds4-server health and restart it if it is wedged","Confirm the network path (local vs remote endpoint) responds; try curl with a manual timeout","Retry the doctor probe once the server is idle"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"const ATTEMPTS: usize = 2;\nfor attempt in 1..=ATTEMPTS {\n    match probe_ds4_models(&config).await {\n        Ok(()) => break,\n        Err(error) if attempt < ATTEMPTS && error.to_string().contains(\"timed out\") => {\n            tokio::time::sleep(std::time::Duration::from_secs(2 * attempt as u64)).await;\n        }\n        Err(error) => return Err(error),\n    }\n}","preventionTips":["Check ds4-server load before probing; a server busy loading a model will not answer in 15s","Keep the probe on a local network path when possible; the 15s budget assumes low latency","Treat repeated timeouts as a wedged server: restart it rather than retrying indefinitely"],"tags":["rust","tui","network","timeout","ds4","doctor"],"backgroundTag":"request-timeout","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}