zed-industries/zed · error · anyhow::Error

No completion returned from Codestral

Error message

No completion returned from Codestral

What it means

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.

Source

Thrown at crates/codestral/src/codestral.rs:192

        let codestral_response: CodestralResponse = serde_json::from_str(&body)?;

        let elapsed = start_time.elapsed();

        if let Some(choice) = codestral_response.choices.first() {
            let completion = &choice.message.content;

            log::debug!(
                "Codestral: Completion received ({} tokens, {:.2}s)",
                codestral_response.usage.completion_tokens,
                elapsed.as_secs_f64()
            );

            // Return just the completion text for insertion at cursor
            Ok(completion.clone())
        } else {
            log::error!("Codestral: No completion returned in response");
            Err(anyhow::anyhow!("No completion returned from Codestral"))
        }
    }
}

impl EditPredictionDelegate for CodestralEditPredictionDelegate {
    fn name() -> &'static str {
        "codestral"
    }

    fn display_name() -> &'static str {
        "Codestral"
    }

    fn show_predictions_in_menu() -> bool {
        true
    }

    fn icons(&self, _cx: &App) -> EditPredictionIconSet {

View on GitHub (pinned to bc538def45)

Solutions

  1. 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
  2. Verify the FIM prompt actually contains non-trivial prefix and suffix text; skip the API call client-side when either is empty
  3. Pass an explicit max_tokens larger than 350 (the default set at codestral.rs:137) when editing large code regions
  4. 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)
  5. 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

Example fix

// before
let codestral_response: CodestralResponse = serde_json::from_str(&body)?;
if let Some(choice) = codestral_response.choices.first() {
    Ok(choice.message.content.clone())
} else {
    Err(anyhow::anyhow!("No completion returned from Codestral"))
}

// after: treat empty completion as an empty insertion instead of a hard error
let codestral_response: CodestralResponse = serde_json::from_str(&body)?;
match codestral_response.choices.first() {
    Some(choice) => Ok(choice.message.content.clone()),
    None => {
        log::warn!("Codestral: empty choices array in response");
        Ok(String::new())
    }
}
Defensive patterns

Strategy: fallback

Validate before calling

// Skip the API call when prefix or suffix carry no signal
if prompt.trim().is_empty() && suffix.trim().is_empty() {
    return Ok(String::new());
}
let max_tokens = Some(max_tokens.unwrap_or(350).max(64));

Type guard

fn has_completion(response: &CodestralResponse) -> bool {
    response.choices.first().is_some()
}

Try / catch

// In the edit-prediction caller: degrade to no prediction, never surface to the user
match delegate.complete(prompt, suffix, max_tokens, model).await {
    Ok(text) => Some(text),
    Err(err) if err.to_string().contains("No completion returned from Codestral") => {
        log::warn!("codestral returned no completion, skipping prediction");
        None
    }
    Err(err) => {
        log::error!("codestral completion failed: {err:#}");
        None
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/5ce497accf86f95a. Report an issue: GitHub.