{"record":{"id":"4e9b0343b32b8d61","repo":"Hmbown/CodeWhale","slug":"ds4-v1-models-could-not-be-reached-at-is-ds4","errorCode":null,"errorMessage":"DS4 /v1/models could not be reached at {} (is ds4-server running?)","messagePattern":"DS4 /v1/models could not be reached at (.+?) \\(is ds4-server running\\?\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/doctor.rs","lineNumber":410,"sourceCode":"}\n\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 {","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/doctor.rs#L392-L428","documentation":"Same DS4 probe path as [481], but `ds4_probe_error` found no `HTTP <status>` token in the transport error text (doctor.rs:453-456) — the request never got an HTTP response. The doctor concludes the redacted endpoint is unreachable and asks whether ds4-server is running.","triggerScenarios":"Nothing is listening on the configured host/port (connection refused); ds4-server is still booting; DNS or routing failure; wrong port in `deepseek_base_url`.","commonSituations":"Doctor run before ds4-server starts; the service crashed silently; config still pointing at a port from an older install or a different machine.","solutions":["Start ds4-server and confirm it listens on the configured port (`ss -ltnp` / `lsof -i` / service status)","Correct the host and port in `deepseek_base_url`","Re-run the doctor once the service accepts connections"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let url = reqwest::Url::parse(&config.deepseek_base_url())?;\nlet host = url.host_str().context(\"base URL has no host\")?;\nlet port = url.port_or_known_default().context(\"no port\")?;\ntokio::time::timeout(\n    std::time::Duration::from_secs(3),\n    tokio::net::TcpStream::connect((host, port)),\n).await\n.context(\"ds4-server is not accepting connections\")??;","typeGuard":null,"tryCatchPattern":"match probe_ds4(config).await {\n    Err(err) if err.to_string().starts_with(\"DS4 /v1/models could not be reached\") => {\n        eprintln!(\"start ds4-server, then re-run: {err}\");\n    }\n    other => other?,\n}","preventionTips":["Order startup so ds4-server is healthy before the doctor runs","Supervise ds4-server (restart on exit) so silent crashes surface","Keep the base URL host/port identical across environments"],"tags":["network","connection","diagnostics","deepseek","doctor"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}