{"record":{"id":"f393758601ffa745","repo":"zed-industries/zed","slug":"custom-server-error","errorCode":null,"errorMessage":"custom server error: {} - {}","messagePattern":"custom server error: (.+?) - (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/edit_prediction/src/open_ai_compatible.rs","lineNumber":117,"sourceCode":"                .method(http_client::Method::POST)\n                .uri(settings.api_url.as_ref())\n                .header(\"Content-Type\", \"application/json\");\n\n            if let Some(api_key) = api_key {\n                http_request_builder =\n                    http_request_builder.header(\"Authorization\", format!(\"Bearer {}\", api_key));\n            }\n\n            let http_request =\n                http_request_builder.body(http_client::AsyncBody::from(request_body))?;\n\n            let mut response = http_client.send(http_request).await?;\n            let status = response.status();\n\n            if !status.is_success() {\n                let mut body = String::new();\n                response.body_mut().read_to_string(&mut body).await?;\n                anyhow::bail!(\"custom server error: {} - {}\", status, body);\n            }\n\n            let mut body = String::new();\n            response.body_mut().read_to_string(&mut body).await?;\n\n            let parsed: RawCompletionResponse =\n                serde_json::from_str(&body).context(\"Failed to parse completion response\")?;\n            let text = parsed\n                .choices\n                .into_iter()\n                .next()\n                .map(|choice| choice.text)\n                .unwrap_or_default();\n            Ok((text, parsed.id))\n        }\n    }\n}\n","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/edit_prediction/src/open_ai_compatible.rs#L99-L135","documentation":"OpenAI-compatible provider client used for user-configured custom LLM servers: any non-2xx completion response reads the body and bails as `custom server error: {status} - {body}` (open_ai_compatible.rs:117). Because the server is arbitrary, the body may be a provider JSON error or a plain HTML error page — both are surfaced verbatim.","triggerScenarios":"A custom server completion request returning 401 (bad API key), 404 (base URL missing `/v1` or wrong path), 400 (model/chat/completions not supported), 429, or an HTML 502 from a gateway in front of the model server.","commonSituations":"Pointing Zed at Ollama/LM Studio/vLLM/LiteLLM with a wrong `api_url` (missing `/v1`), a model id the server does not host, an expired key, or a local server that is not actually OpenAI-compatible.","solutions":["Reproduce with curl against the same URL, key, and model to see the raw server response.","Fix the base URL to include the versioned prefix (e.g. `http://localhost:11434/v1`) and use the chat-completions path the server implements.","Confirm the API key header the server expects (Bearer vs custom) and that the model id exists on that server.","For 5xx HTML bodies, fix the gateway/reverse-proxy in front of the model server."],"exampleFix":"// before (Zed settings — wrong api_url, server answers 404)\n\"api_url\": \"http://localhost:11434\"\n\n// after (OpenAI-compatible base path)\n\"api_url\": \"http://localhost:11434/v1\"","handlingStrategy":"validation","validationCode":"# smoke-test the custom server exactly as Zed will call it\ncurl -sS \"$ZED_API_URL/chat/completions\" \\\n  -H \"Authorization: Bearer $ZED_API_KEY\" -H 'Content-Type: application/json' \\\n  -d '{\"model\":\"'$ZED_MODEL'\",\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}' \\\n  || echo \"fix api_url (must include /v1), key, or model before use\"","typeGuard":"fn looks_like_openai_base_url(url: &str) -> bool {\n    let path = url.split_once(\"://\").map(|(_, rest)| rest).unwrap_or(url);\n    !path.is_empty() && !url.contains(' ') && (path.ends_with(\"/v1\") || path.split('/').count() >= 2)\n}","tryCatchPattern":"Catch the bail and parse `{status} - {body}`: for 404 print a hint about the missing `/v1` prefix, for 401 about the API key, for HTML bodies about the reverse proxy; only retry on 5xx/429.","preventionTips":["Always include the versioned base path (`/v1`) in api_url.","Verify the model id exists on the server with curl before configuring Zed.","Confirm the server actually implements the OpenAI chat-completions schema, not merely a similar one."],"tags":["openai-compatible","llm","http","configuration","custom-server"],"backgroundTag":"openai-compatible-api-error","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}