{"record":{"id":"5ce497accf86f95a","repo":"zed-industries/zed","slug":"no-completion-returned-from-codestral","errorCode":null,"errorMessage":"No completion returned from Codestral","messagePattern":"No completion returned from Codestral","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codestral/src/codestral.rs","lineNumber":192,"sourceCode":"\n        let codestral_response: CodestralResponse = serde_json::from_str(&body)?;\n\n        let elapsed = start_time.elapsed();\n\n        if let Some(choice) = codestral_response.choices.first() {\n            let completion = &choice.message.content;\n\n            log::debug!(\n                \"Codestral: Completion received ({} tokens, {:.2}s)\",\n                codestral_response.usage.completion_tokens,\n                elapsed.as_secs_f64()\n            );\n\n            // Return just the completion text for insertion at cursor\n            Ok(completion.clone())\n        } else {\n            log::error!(\"Codestral: No completion returned in response\");\n            Err(anyhow::anyhow!(\"No completion returned from Codestral\"))\n        }\n    }\n}\n\nimpl EditPredictionDelegate for CodestralEditPredictionDelegate {\n    fn name() -> &'static str {\n        \"codestral\"\n    }\n\n    fn display_name() -> &'static str {\n        \"Codestral\"\n    }\n\n    fn show_predictions_in_menu() -> bool {\n        true\n    }\n\n    fn icons(&self, _cx: &App) -> EditPredictionIconSet {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/codestral/src/codestral.rs#L174-L210","documentation":"Thrown by the Codestral edit-prediction delegate when a POST to {api_url}/v1/fim/completions returns HTTP 200 but the parsed CodestralResponse contains an empty `choices` array (crates/codestral/src/codestral.rs:179-193). The request itself succeeded at the transport and auth level; the model simply produced no fill-in-the-middle (FIM) completion for the given prompt/suffix. The code only looks at choices.first(), so a single empty choice list aborts the whole call.","triggerScenarios":"Calling CodestralEditPredictionDelegate::complete (or the underlying completion fn at codestral.rs:120) with: an empty or whitespace-only prompt/suffix; max_tokens left None (defaults to 350) or set too low for the model to emit anything; a prompt not formatted with the FIM tokens the model expects; or a server-side content filter / safety refusal that returns 200 with zero choices. Any non-2xx status takes the earlier 'Codestral API error' branch instead, so this error specifically means 2xx + empty choices.","commonSituations":"Zed users with a Mistral/Codestral API key hitting context edges: completion requested at end-of-file with empty suffix, at the very first character with empty prefix, in files with unsupported languages, or with stale/custom api_url endpoints (e.g. a proxy) that return a 200 response shaped differently. Also seen after Mistral rotates model names and the configured model silently degrades to empty output.","solutions":["Log the raw response body (add a debug print of `body` before the `if let Some(choice)` at codestral.rs:179) to see whether choices is truly empty vs. malformed JSON that serde defaulted into an empty Vec","Verify the FIM prompt actually contains non-trivial prefix and suffix text; skip the API call client-side when either is empty","Pass an explicit max_tokens larger than 350 (the default set at codestral.rs:137) when editing large code regions","Confirm the model name configured is a FIM-capable Codestral model and the api_url points at a real Mistral FIM endpoint (path is hardcoded to /v1/fim/completions at codestral.rs:152)","If the endpoint is behind a proxy, capture one request/response pair with a debug proxy to confirm the choices array is present on the wire"],"exampleFix":"// before\nlet codestral_response: CodestralResponse = serde_json::from_str(&body)?;\nif let Some(choice) = codestral_response.choices.first() {\n    Ok(choice.message.content.clone())\n} else {\n    Err(anyhow::anyhow!(\"No completion returned from Codestral\"))\n}\n\n// after: treat empty completion as an empty insertion instead of a hard error\nlet codestral_response: CodestralResponse = serde_json::from_str(&body)?;\nmatch codestral_response.choices.first() {\n    Some(choice) => Ok(choice.message.content.clone()),\n    None => {\n        log::warn!(\"Codestral: empty choices array in response\");\n        Ok(String::new())\n    }\n}","handlingStrategy":"fallback","validationCode":"// Skip the API call when prefix or suffix carry no signal\nif prompt.trim().is_empty() && suffix.trim().is_empty() {\n    return Ok(String::new());\n}\nlet max_tokens = Some(max_tokens.unwrap_or(350).max(64));","typeGuard":"fn has_completion(response: &CodestralResponse) -> bool {\n    response.choices.first().is_some()\n}","tryCatchPattern":"// In the edit-prediction caller: degrade to no prediction, never surface to the user\nmatch delegate.complete(prompt, suffix, max_tokens, model).await {\n    Ok(text) => Some(text),\n    Err(err) if err.to_string().contains(\"No completion returned from Codestral\") => {\n        log::warn!(\"codestral returned no completion, skipping prediction\");\n        None\n    }\n    Err(err) => {\n        log::error!(\"codestral completion failed: {err:#}\");\n        None\n    }\n}","preventionTips":["Never call the FIM endpoint with empty prefix AND suffix","Set an explicit max_tokens >= expected completion length instead of relying on the 350 default","Log the raw response body once when adding new api_url/model configurations to catch shape drift early"],"tags":["codestral","llm","completion","api","zed"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}