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

DeepSeek does not support custom tools

Error message

DeepSeek does not support custom tools

What it means

into_deepseek rejects any LanguageModelRequest with contains_custom_tool_input() before building the OpenAI-compatible chat request: DeepSeek's API only accepts function tools with JSON schemas, so the freeform custom-tool variant cannot be mapped to a tool entry.

Source

Thrown at crates/language_models/src/provider/deepseek.rs:366

            Err(error) => return async move { Err(error.into()) }.boxed(),
        };
        let stream = self.stream_completion(request, cx);

        async move {
            let mapper = DeepSeekEventMapper::new();
            Ok(mapper.map_stream(stream.await?).boxed())
        }
        .boxed()
    }
}

pub fn into_deepseek(
    request: LanguageModelRequest,
    model: &deepseek::Model,
    max_output_tokens: Option<u64>,
) -> Result<deepseek::Request> {
    if request.contains_custom_tool_input() {
        anyhow::bail!("DeepSeek does not support custom tools");
    }

    let thinking = deepseek_thinking(model, request.thinking_allowed);
    let thinking_enabled = thinking
        .as_ref()
        .is_some_and(|thinking| thinking.kind == deepseek::ThinkingType::Enabled);

    let mut messages = Vec::new();
    let mut current_reasoning: Option<String> = None;

    for message in request.messages {
        for content in message.content {
            match content {
                MessageContent::Text(text) => {
                    let should_add = if message.role == Role::User {
                        !text.trim().is_empty()
                    } else {
                        !text.is_empty()

View on GitHub (pinned to f4178619ac)

Solutions

  1. Convert the tool to a function tool with a JSON Schema input
  2. Disable the custom tool/extension when the active model is DeepSeek
  3. Use a provider whose API supports custom tools for that workflow
  4. Check the request's tools with contains_custom_tool_input() before dispatch
Defensive patterns

Strategy: validation

Validate before calling

if model.provider() == "deepseek" && request.contains_custom_tool_input() {
    return Err(anyhow!("DeepSeek requires function tools; re-register '{}' with a JSON schema", tool_name));
}

Type guard

fn is_function_only_request(request: &LanguageModelRequest) -> bool {
    !request.contains_custom_tool_input()
}

Try / catch

match into_deepseek(request, model, max_tokens) {
    Ok(req) => Ok(client.complete(req).await?),
    Err(e) if e.to_string().contains("custom tools") => { request.tools.clear_of_custom(); into_deepseek(request, model, max_tokens) }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Using a DeepSeek model (deepseek-chat/reasoner via Zed's provider integration) while an agent or extension attaches a custom tool to the request.

Common situations: Switching an existing agent from Anthropic/OpenAI to DeepSeek without re-checking tool types; extensions that always register their tool as Custom.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/58ec4b62382a8063. Report an issue: GitHub.