{"record":{"id":"db12e3d98582b868","repo":"Hmbown/CodeWhale","slug":"ds4-v1-models-returned-http-status-at","errorCode":null,"errorMessage":"DS4 /v1/models returned HTTP {status} at {}","messagePattern":"DS4 /v1/models returned HTTP (.+?) at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/doctor.rs","lineNumber":579,"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::CodewhaleClient;\n    use crate::core::model_client::ModelClient;\n\n    let endpoint = crate::client::redact_url_for_display(&config.active_route_base_url());\n    let client = CodewhaleClient::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":561,"sourceCodeEnd":597,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/doctor.rs#L561-L597","documentation":"`probe_ds4_models` (doctor check) calls the DeepSeek Harness 4 endpoint's `/v1/models` through `client.list_models()`. If the client returns an error (as opposed to timing out), the probe bails with a formatted probe error including the endpoint. This indicates the DS4 endpoint is reachable-configured but rejected or failed the models request.","triggerScenarios":"Running the doctor's DS4 probe when `client.list_models()` yields Err — e.g. HTTP 401/404 from the endpoint, TLS failure, malformed base URL, or connection reset within the 15-second window.","commonSituations":"Wrong DS4 base URL or port; endpoint requires an API key the client lacks; endpoint does not implement `/v1/models`; self-signed or expired TLS certificate; local DS4 server not started.","solutions":["Run the doctor again and read `ds4_probe_error`'s message, which embeds the underlying client error, then fix that root cause.","Verify the DS4 endpoint URL and that the server is running (`curl <endpoint>/v1/models`).","Check that the API key/credentials for DS4 are set and valid.","If TLS-related, fix or trust the endpoint's certificate chain."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before running the doctor probe, check the endpoint yourself\ncurl -sS --max-time 10 -H \"Authorization: Bearer $DS4_KEY\" \"$DS4_ENDPOINT/v1/models\" | head -c 200","typeGuard":null,"tryCatchPattern":"match doctor_probe().await {\n    Err(e) if e.to_string().contains(\"DS4 /v1/models\") && !e.to_string().contains(\"timed out\") => {\n        // read embedded ds4_probe_error text and fix endpoint/key/TLS\n    }\n    other => other?,\n}","preventionTips":["Validate the DS4 endpoint URL and credentials whenever configuration changes.","Start the DS4 server before running doctor checks.","Use HTTPS endpoints with valid certificates."],"tags":["ds4","deepseek","http","doctor","models-endpoint"],"backgroundTag":"api-error-response","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}