Every one of these tools supports a custom OpenAI-compatible endpoint. That's not the interesting part. The interesting part is that the field is in five different places, three of them undocumented in the UI, and each tool fails in a way that doesn't look like a configuration error.
I run a gateway, so I see the support tickets from all five. Disclosure: I work on daoxe. Nothing below is specific to it — swap in any endpoint that speaks /v1/chat/completions. Menu paths in desktop tools drift every few releases, so the paths below were re-verified against each vendor's current documentation on 2026-09-09; if a path has moved since, the gotcha underneath it almost certainly hasn't.
Deliberately not covered: GitHub Copilot. Its BYOK flow is a different mechanism with different limits and it deserves its own write-up. Everything here is the "custom base URL" family.
Ground rule: never type a model ID from memory
Before touching any tool, get the exact strings your key can call:
curl -s https://api.daoxe.com/v1/models \
-H "Authorization: Bearer $LLM_API_KEY" | python3 -m json.tool | grep '"id"'
Most of the failures below trace back to an ID that is almost right. claude-sonnet-4-5 and claude-sonnet-4-5-20250929 are not interchangeable, and a tool that gets a 404 from a model list it can't see will usually surface it as "connection failed".
And those IDs are a moving target in their own right — vendors rename, deprecate, and quietly re-point them. I wrote up that side of the problem — pinning IDs, detecting drift, what to log when the model field stops matching what you asked for — in Model IDs are a dependency. Pin them like one..
Cursor
Where: Settings → Models. Enter your key in the OpenAI API Key box, expand it, enable Override OpenAI Base URL, and set it to your endpoint's /v1 root. Then add your model with + Add model, typing the exact ID.
Three gotchas, in the order people hit them:
- Verification calls a model, not the base URL. The "Verify" button issues a real completion. If the default OpenAI model names are still enabled in the list and your endpoint doesn't serve those exact IDs, verification fails even though your custom model works perfectly. Add your model first, toggle the stock ones off, then verify.
- A custom key does not make every feature custom. Chat and inline edit go to your endpoint. Tab completion and the editor's own indexing/retrieval features are Cursor-side services and keep working the way they always did. This surprises people who expected the custom key to cover their entire bill.
- Traffic shape isn't obvious. Depending on your privacy settings, requests may still transit Cursor's infrastructure rather than going browser-direct to your endpoint. If that matters to you — corporate egress rules, IP allowlisting on the endpoint side — test it with a throwaway key before you assume.
Symptom of a wrong ID: a generic red toast with no upstream error text. Cursor swallows the body.
Cline
Where: the Cline sidebar → settings gear → API Provider → OpenAI Compatible. Fill Base URL (.../v1), API Key, Model. Then open the model configuration section underneath, which is where the real work is.
- Fill in the context window yourself. For a model Cline doesn't recognise, it can't infer the window, and its context manager will either truncate too early (you lose file content mid-task) or too late (the upstream returns a context-length error halfway through a multi-file edit). This field is the single highest-value thing on the page.
- Set the image-support flag honestly. Turning it on for a text-only model produces a 400 the first time you paste a screenshot, several steps into a task.
-
Malformed diffs are a model problem wearing a config costume. "The SEARCH block doesn't match" repeated three times in a row usually means the model behind your ID isn't strong enough at exact-match diff editing, not that the endpoint is broken. Try the same ID via
curlwith a diff-editing prompt before you blame the wiring. - Cline shows a per-request cost estimate derived from the price fields you type in. Those are local display values; they don't come from the endpoint and they don't affect billing.
Nice property: Cline surfaces the raw request in an expandable block in the chat — when something's wrong, look there first; it's the best request log of the five. If your build doesn't show it, the proxy trick at the end of this article covers you.
Continue
Where: ~/.continue/config.yaml (older installs use config.json).
name: gateway
version: 0.0.1
schema: v1
models:
- name: Deep model
provider: openai
model: <exact-id-from-/v1/models>
apiBase: https://api.daoxe.com/v1
apiKey: ${{ secrets.LLM_API_KEY }}
roles: [chat, edit, apply]
- name: Embeddings
provider: openai
model: <exact-embedding-id>
apiBase: https://api.daoxe.com/v1
apiKey: ${{ secrets.LLM_API_KEY }}
roles: [embed]
-
provider: openaiplusapiBaseis the generic path. You are not claiming the model is from OpenAI; you're telling Continue which wire protocol to speak. Include/v1inapiBase— Continue appends/chat/completionsto whatever you give it, and a missing/v1produces a 404 that looks like an auth failure. -
Do not give a chat model the
autocompleterole. This is the most expensive mistake in this article. Autocomplete fires continuously as you type and expects a fill-in-the-middle model; a chat model will return prose, Continue will show nonsense ghost text, and you'll generate a request every few keystrokes. If you have no FIM-capable model, leave autocomplete unconfigured. -
embedis separate. If@codebasereturns irrelevant chunks, check that an embeddings model is actually configured rather than silently falling back.
Where to look when it breaks: enable the Continue: Enable Console setting and run Focus on Continue Console View from the command palette for prompt logs; the core log lives at ~/.continue/logs/core.log.
Zed
Where: zed: open settings (the JSON file) — or, in recent builds, agent: open settings → Add Provider under LLM Providers, which writes the same block for you. Zed does not discover models — you declare them, either way.
{
"language_models": {
"openai_compatible": {
"gateway": {
"api_url": "https://api.daoxe.com/v1",
"available_models": [
{
"name": "<exact-id-from-/v1/models>",
"display_name": "Deep model",
"max_tokens": 200000,
"max_output_tokens": 8192,
"capabilities": { "tools": true, "images": false }
}
]
}
}
}
}
-
The API key is not in this file. Zed resolves the key from an environment variable named after the provider (
DAOXE_API_KEYfor a provider iddaoxe) — set it in your shell or launch environment. A perfectly correctsettings.jsonwith no key set produces "no models available" and no error. -
max_tokensis the context window and it is mandatory. Zed has no catalogue to look it up in. Guess high and you get upstream 400s on long sessions; guess low and Zed refuses to attach files it could have. -
Declaring
"tools": truefor a model that doesn't do tool calling gives you an agent that opens a thread, thinks, and does nothing. Check tool support against the endpoint first. - The provider key under
language_modelshas been renamed at least once across Zed versions. If the block above is rejected, runzed: open default settingsand copy the current key name from there rather than trusting any blog post, including this one.
Aider
Where: environment variables, CLI flags, or .aider.conf.yml. All three work; pick one and stick to it.
export OPENAI_API_BASE=https://api.daoxe.com/v1
export OPENAI_API_KEY=sk-...
aider --model openai/<exact-id> --weak-model openai/<cheap-exact-id>
Or, checked into the repo root as .aider.conf.yml:
model: openai/<exact-id>
weak-model: openai/<cheap-exact-id>
openai-api-base: https://api.daoxe.com/v1
edit-format: diff
-
The
openai/prefix is not decoration. Aider routes through LiteLLM, and the prefix is what says "treat this as a generic OpenAI-protocol endpoint". Drop it and LiteLLM tries to pattern-match your ID against its own registry, which can send the request somewhere else entirely or attach parameters your upstream rejects. -
--weak-modelis the classic delayed failure. Aider uses a second, cheaper model for commit messages and chat summarisation. If you don't set it, the default may not exist on your endpoint — and because it's only used after your first successful edit, everything looks fine right up until the commit step. Set it explicitly. -
Unknown models trigger metadata warnings. Silence them properly with
.aider.model.metadata.jsonin the repo root:
{
"openai/<exact-id>": {
"max_input_tokens": 200000,
"max_output_tokens": 8192,
"input_cost_per_token": 0,
"output_cost_per_token": 0,
"litellm_provider": "openai",
"mode": "chat"
}
}
Leaving the cost fields at 0 disables aider's local spend estimate, which is better than a wrong estimate. Check your real usage on the provider's side.
- If streaming misbehaves through a corporate proxy,
--no-streamisolates it in one flag.
Symptom → cause
| What you see | Where it usually comes from |
|---|---|
| "Connection failed" with no detail | Wrong model ID, or apiBase missing /v1
|
| Works, then dies at commit / summary time | Aider --weak-model not set |
| Ghost text is prose, token use spikes | Chat model given Continue's autocomplete role |
| Agent opens a thread and stalls |
"tools": true declared for a non-tool model (Zed) |
| Context errors mid-task, only on big files | Context window not set (Cline) or max_tokens wrong (Zed) |
| "SEARCH block doesn't match", repeatedly | Model capability, not configuration |
| Verify button fails, manual calls work | Cursor validating a stock model ID your endpoint doesn't serve |
| No models listed, config looks perfect | Zed key not entered in the panel UI |
When the tool won't tell you what it sent
Three of these five hide the request body. The fastest way out is to put something in the middle: run a local HTTP proxy, point the tool's base URL at http://127.0.0.1:8080/v1, and forward to the real endpoint. Ten minutes of setup answers questions that hours of toggling settings won't — particularly "is it even sending the model ID I typed" and "which extra parameters is this tool adding".
The part that isn't configuration
All five of these will connect in under five minutes once you know where the field is. What actually determines whether the setup is good is whether the model behind that ID does diff editing well, holds a long tool-use loop without drifting, and returns the context window it claims. That's an evaluation problem, and it's the one worth your afternoon — the config is a ten-line diff.
One practical consequence of doing it this way: the model becomes a config string in five files instead of five separate integrations, so trying a different one next month is an edit, not a migration. That's the whole reason to route through a single endpoint, and it's a much more honest reason than any benchmark table.
This post is part of an ongoing "verify your endpoint" series. Once you've pointed a tool at an endpoint, the next question is whether it actually honours the OpenAI wire format — streaming shape, tool calls, error bodies — which is what "OpenAI-compatible" is a spectrum, not a boolean tests with an eleven-check script. And because every setup above hinges on an exact model ID, Model IDs are a dependency. Pin them like one. covers the catalogue side: pinning, drift detection, and what to log when the model field stops matching.
Top comments (0)