{"record":{"id":"e127f90058306ca8","repo":"zed-industries/zed","slug":"lm-studio-does-not-support-custom-tools","errorCode":null,"errorMessage":"LM Studio does not support custom tools","messagePattern":"LM Studio does not support custom tools","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/language_models/src/provider/lmstudio.rs","lineNumber":338,"sourceCode":"        ))\n    }\n}\n\npub struct LmStudioLanguageModel {\n    id: LanguageModelId,\n    model: lmstudio::Model,\n    http_client: Arc<dyn HttpClient>,\n    request_limiter: RateLimiter,\n    state: Entity<State>,\n}\n\nimpl LmStudioLanguageModel {\n    fn to_lmstudio_request(\n        &self,\n        request: LanguageModelRequest,\n    ) -> Result<lmstudio::ChatCompletionRequest> {\n        if request.contains_custom_tool_input() {\n            anyhow::bail!(\"LM Studio does not support custom tools\");\n        }\n\n        let mut messages = Vec::new();\n\n        for message in request.messages {\n            for content in message.content {\n                match content {\n                    MessageContent::Text(text) => add_message_content_part(\n                        lmstudio::MessagePart::Text { text },\n                        message.role,\n                        &mut messages,\n                    ),\n                    MessageContent::Thinking { .. } => {}\n                    MessageContent::RedactedThinking(_) => {}\n                    MessageContent::Compaction(_) => {}\n                    MessageContent::Image(image) => {\n                        add_message_content_part(\n                            lmstudio::MessagePart::Image {","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/zed-industries/zed/blob/5a9b9558db01a6b906cec2fb70a797affdc58cdd/crates/language_models/src/provider/lmstudio.rs#L320-L356","documentation":"LmStudioLanguageModel::to_lmstudio_request applies the same guard as the other OpenAI-compatible converters: LM Studio's API models function tools only, so contains_custom_tool_input() == true aborts request construction before any messages are serialized.","triggerScenarios":"Selecting an LM Studio-hosted local model while the request carries a custom (non-function) tool — typically an agent or extension that registered a Custom tool input.","commonSituations":"Local LM Studio setups combined with agents authored for cloud providers; toggling a tool-bearing assistant between providers; extension updates introducing custom tools into existing flows.","solutions":["Register the tool as a function tool with a JSON Schema input instead","Disable the custom tool/extension for LM Studio models","Route tool-heavy agents to a provider with custom-tool support","Pre-check contains_custom_tool_input() in your dispatch layer and degrade gracefully"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if self.model.is_lmstudio() && request.contains_custom_tool_input() {\n    anyhow::bail!(\"LM Studio accepts function tools only\");\n}","typeGuard":"fn is_function_only_request(request: &LanguageModelRequest) -> bool {\n    !request.contains_custom_tool_input()\n}","tryCatchPattern":"match self.to_lmstudio_request(request) {\n    Ok(req) => Ok(self.stream(req).await?),\n    Err(e) if e.to_string().contains(\"custom tools\") => self.stream(request_without_custom_tools()).await,\n    Err(e) => Err(e),\n}","preventionTips":["Gate custom tools on provider capability checks in your tool registry","Default to function tools with JSON Schemas for portability across local servers","Fail with an actionable message at tool-registration time instead of mid-request"],"tags":["lm-studio","llm","local-models","tools","capability-mismatch"],"backgroundTag":"llm-provider-unsupported-tool-type","analyzedSha":"5a9b9558db01a6b906cec2fb70a797affdc58cdd","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}