{"record":{"id":"6865824179a3bf86","repo":"Hmbown/CodeWhale","slug":"fim-api-error-http-status-error-text","errorCode":null,"errorMessage":"FIM API error: HTTP {status}: {error_text}","messagePattern":"FIM API error: HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":3631,"sourceCode":"        }\n        let url = api_url_with_suffix(&self.base_url, \"beta/completions\", None);\n        let model = wire_model_for_provider_route(self.api_provider, &self.base_url, model);\n        let body = json!({\n            \"model\": model,\n            \"prompt\": prompt,\n            \"suffix\": suffix,\n            \"max_tokens\": max_tokens,\n        });\n        let response = self.send_json_with_retry(&url, &body).await?;\n        let status = response.status();\n        if !status.is_success() {\n            let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;\n            let error_text = sanitize_http_error_body(\n                Some(self.api_provider.display_name()),\n                status.as_u16(),\n                &raw_error_text,\n            );\n            anyhow::bail!(\"FIM API error: HTTP {status}: {error_text}\");\n        }\n        let response_text = response\n            .text()\n            .await\n            .context(\"Failed to read FIM API response body\")?;\n        let value: serde_json::Value =\n            serde_json::from_str(&response_text).context(\"Failed to parse FIM API response\")?;\n        let text = value\n            .pointer(\"/choices/0/text\")\n            .and_then(serde_json::Value::as_str)\n            .ok_or_else(|| anyhow::anyhow!(\"FIM response missing choices[0].text\"))?;\n        Ok(text.to_string())\n    }\n}\n\nmod anthropic;\nmod chat;\npub(crate) mod cloud_code;","sourceCodeStart":3613,"sourceCodeEnd":3649,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/client.rs#L3613-L3649","documentation":"`fim_completion` (the fill-in-the-middle autocomplete backing the FIM tool) posts `{model, prompt, suffix, max_tokens}` to `{base_url}/beta/completions` — DeepSeek's FIM endpoint — and bails on any non-2xx status after the retrying transport gives up. Earlier guards already rejected non-chat-completions wire formats and the OpencodeZen provider, so this error means the endpoint answered but with an error status; the body (64KB cap) is sanitized with the provider display name before inclusion.","triggerScenarios":"401/403 wrong API key; 404 when the base_url's server does not implement `/beta/completions` (OpenAI, most OpenAI-compatible proxies); 400 when the model id is a chat model without FIM support; 429/5xx rate limits and outages.","commonSituations":"Enabling the FIM autocomplete tool while pointed at a generic OpenAI-compatible gateway that only implements `/chat/completions`; using `deepseek-chat` instead of a FIM-capable model; stale API key; base_url missing or doubling the `/v1` prefix so `/v1/beta/completions` does not resolve.","solutions":["Point the FIM tool's provider/base_url at a server that implements DeepSeek's `/beta/completions` FIM surface and use a FIM-capable model.","Check the status in the message: 401 → fix the key; 404 → the endpoint does not exist on this base_url; 400 → wrong model for FIM.","Verify with curl: `curl -s $BASE_URL/beta/completions -H \"Authorization: Bearer $KEY\" -d '{\"model\":\"<fim-model>\",\"prompt\":\"a\",\"suffix\":\"b\",\"max_tokens\":8}'`.","If the provider cannot serve FIM, disable the FIM tool rather than letting every keystroke trigger this error."],"exampleFix":"# before\n[fim]\nmodel = \"deepseek-chat\"\n# after\n[fim]\nmodel = \"deepseek-coder\"   # FIM-capable model served by /beta/completions","handlingStrategy":"try-catch","validationCode":"// Preflight the FIM endpoint once before enabling autocomplete.\nasync fn fim_endpoint_ok(base_url: &str, key: &str, model: &str) -> bool {\n    let url = format!(\"{base_url}/beta/completions\");\n    reqwest::Client::new().post(&url).bearer_auth(key)\n        .json(&serde_json::json!({\"model\": model, \"prompt\": \"a\", \"suffix\": \"b\", \"max_tokens\": 4}))\n        .send().await.is_ok_and(|r| r.status().is_success())\n}","typeGuard":"fn fim_supported(provider: ApiProvider, wire: WireFormat) -> bool {\n    provider != ApiProvider::OpencodeZen && wire == WireFormat::ChatCompletions\n}","tryCatchPattern":"match client.fim_completion(&model, prompt, suffix, max_tokens).await {\n    Ok(text) => render_completion(text),\n    Err(e) if e.to_string().contains(\"FIM API error: HTTP 404\") => disable_fim_tool(\"endpoint has no /beta/completions\"),\n    Err(e) if e.to_string().contains(\"HTTP 401\") => prompt_for_api_key(),\n    Err(e) => log_and_suppress(e), // never block typing on an autocomplete failure\n}","preventionTips":["Disable the FIM tool by default unless a preflight probe of `/beta/completions` succeeds.","Keep a dedicated FIM provider config (DeepSeek-compatible base_url + FIM model) separate from chat providers.","Autocomplete failures must never surface as user-facing errors — log, back off, and stop retrying."],"tags":["fim","autocomplete","http","api","deepseek","configuration"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}