Empty model picker on a custom endpoint: read /v1/models first
A working key and a working URL do not guarantee a populated dropdown. The picker is fed by a separate route, and some clients do not fetch it at all — so the empty box tells you nothing about whether your configuration is correct. The endpoint's own model list is the only authority, and it is one curl away.
https://aicomp.ai/v1).
Create one free →
curl https://aicomp.ai/v1/models \
-H "Authorization: Bearer sk-your-gateway-key"
A JSON list of model IDs means the key is good. Invalid token means it was copied wrong.
The route that feeds the dropdown
The list endpoint is a normal GET on your base URL, and the response shape is fixed: an object whose object is list, containing a data array of entries each carrying an id.
GET /v1/models
Authorization: Bearer sk-...
200 OK
{
"object": "list",
"data": [
{"id": "gpt-5.6-luna", "object": "model", "created": 1750000000, "owned_by": "openai"},
{"id": "claude-sonnet-5", "object": "model", "created": 1750000001, "owned_by": "anthropic"}
]
}
That id is the only string the completion route will accept as model. Not the display name, not the vendor's marketing name, not the slug in a documentation URL.
# The endpoint's own list is the only authoritative source of model ids.
curl -s https://aicomp.ai/v1/models -H "Authorization: Bearer $GATEWAY_API_KEY" > /tmp/models.json
head -c 200 /tmp/models.json
# -> starts with {"object":"list" ... good
# -> starts with <!DOCTYPE or <html something else answered; fix the URL first
python3 - <<'PY'
import json
d = json.load(open("/tmp/models.json"))
ids = [m.get("id") for m in d.get("data", [])]
print(len(ids), "models returned")
print("\n".join(str(i) for i in ids[:10]))
PY
Run it once and keep the output. Everything else on this page is downstream of what it prints.
Four ways an empty picker happens
They look identical in the UI, and only one of them is actually about your configuration:
- The route is not implemented. The endpoint serves
/chat/completionsand nothing else. The picker stays empty; a hand-typed id works immediately. - Something else answered. A wrong path, a corporate proxy or a captive portal returns HTML with a 200. Check that the body parses as JSON before you trust the status code — this is the same first check as in base URL not working.
- The key is scoped to nothing. Authentication succeeded and
datacame back empty. The credential is fine; its permissions are not. - The client never fetches the list. Some clients populate the picker from the route and some expect you to type the id. Which behaviour you get varies by client and version, so test once rather than assuming — and note that manual entry is the deterministic path in either case.
Why copying the id from the wrong place 404s
Ids get rewritten for the address bar. In our catalogue, 15 of the tracked model ids differ from the slug used in their URL — the dot in gpt-5.6-luna becomes a hyphen in gpt-5-6-luna, for instance. Send the slug and the server sees a model it does not serve.
| Send this (the id) | Not this (the URL slug) |
|---|---|
claude-haiku-4-5-20251001 | claude-haiku-4-5 |
gpt-5.6-sol | gpt-5-6-sol |
gpt-5.6-terra | gpt-5-6-terra |
gpt-5.6-luna | gpt-5-6-luna |
gemini-3.7-flash | gemini-3-7-flash |
deepseek-v4-pro-0813 | deepseek-v4-pro |
deepseek-v4-flash-0731 | deepseek-v4-flash |
glm-5.3 | glm-5-3 |
Showing 8 of 15. The full catalogue exposes 839 ids — too many for any picker, which is the next point.
When the list is too big to be useful
A gateway catalogue runs to 839 entries. A picker that renders all of them is technically working and practically unusable, and it makes the "empty dropdown" failure and the "unusable dropdown" failure look like the same bug. If your client supports pinning a shortlist or typing an id, use it.
| Model | Gateway rate in / out per 1M tokens | Official list in / out per 1M tokens | Diff |
|---|---|---|---|
| gpt-5.6-luna | $0.1 / $0.6 | $0.2 / $1.2 | 50% |
| deepseek-v4-flash | $0.22 / $0.66 | — / — | — |
| gemini-3.7-flash | $0.375 / $1.875 | — / — | — |
Rates checked 2026-09-26. Gateway rates move with upstream promotions — verify the current number in your dashboard before committing to a budget.
Rates checked 2026-09-26. Gateway rates move with upstream promotions — verify the current number in your dashboard before committing to a budget.
The failure that does not announce itself
Here is the expensive one. Clients infer a model's context window from its name. Serve a model under an id the client does not recognise and it may assume a small window and trim your prompt to fit — silently, with no error and a perfectly valid response. You get shorter answers and occasional nonsense, and nothing points at the model id.
Where the client exposes a model configuration section — context window, maximum output tokens, image support, tool support — fill it in explicitly for any custom id. That turns an inference into a stated fact.
Related guides
- One endpoint for every tool — the hub for tool-specific setup
- Cline with a custom endpoint — where the id goes in Cline
- Continue with a custom endpoint — the same pattern in Continue
- Base URL not working — when the URL itself is the problem
- context_length_exceeded — the window the client guessed
- Endpoint error codes — reading a 404 on a model id
Empty-picker failures and what each one is actually telling you
| Symptom | What is actually happening | How to confirm | Fix |
|---|---|---|---|
| Dropdown empty, hand-typed id works | The client did not fetch or could not use /v1/models; the endpoint and key are both fine. | Type an id copied from the route's response. | Use manual entry, and keep the id in configuration rather than inline. |
| Dropdown empty and everything 404s | The URL or the credential is wrong, so no route is reachable at all. | Run the curl above and check the body parses as JSON. | Fix the base URL suffix, then re-read the list. |
| The list returns HTML | Something between you and the endpoint answered — a proxy, a captive portal, a wrong path. | Inspect the first 200 bytes of the response. | Correct the URL or the network path; no client setting will fix this. |
| The list is empty but the key authenticates | The credential is valid but scoped to zero models. | Compare the same request with a key you know has access. | Fix the key's scope rather than the client configuration. |
| Long files get summarised instead of read | The client inferred a small context window from an unrecognised model id and trimmed the prompt. | Check whether the client exposes a context-window override. | Enter the window and max output explicitly for every custom id. |
| A model id copied from a URL 404s | Slugs are rewritten for the address bar; the API compares against the raw id. | Compare against the /v1/models response. | Copy ids from the route or from a rate table that quotes them. |
When this is the wrong move
Four assumptions behind most empty-picker tickets
| Situation | Why it breaks | Do this instead |
|---|---|---|
| You are about to assume your client fetches the list | Whether a client populates its picker from /v1/models varies by client and by version. | Test once with a hand-typed id; prefer manual entry in production. |
| You are about to trust a 200 without reading the body | Proxies and error pages return 200 with HTML. | Confirm the body parses as JSON before drawing any conclusion. |
| You are about to leave the context window to inference | An unrecognised id gets a conservative default and your prompt is trimmed silently. | State the window and max output explicitly for custom ids. |
| You are about to hardcode an id from a documentation URL | Slugs differ from ids, and the difference is invisible until a 404. | Copy from /v1/models only. |
Rolling it out without finding out the hard way
The failure mode you want to avoid is discovering a problem through a production bill or a customer-visible error. Four steps, in order:
- Prove it on one request with the id copied from /v1/models, with the window set explicitly. One call, from a script, with an explicit timeout and the model ID echoed back. You are testing reachability, authentication and model availability — three things that can fail independently.
- Measure before you switch. Record tokens per task and cost per task on the current path first. Without that baseline, "it got cheaper" is an impression, not a result.
- Move one workload, not everything. Pick the workload with the most predictable shape — batch jobs over interactive traffic. Leave the interactive path on the old configuration until the batch numbers are in.
- Decide the rollback condition in advance. Write down what makes you revert (error rate above X, cost per task above Y, latency above Z) before you start, so the decision is not made under pressure.
Keep the base URL in configuration, never inline. That single choice is what makes step four take a minute instead of an afternoon.
FAQ
Why is the dropdown empty when my key and URL both work?
Because the picker and the credential are independent. Several clients populate their model dropdown from GET /v1/models; if that route is not implemented, returns an empty data array, or is answered by something that is not the API, the picker stays empty while a hand-typed id would work perfectly. Which clients fetch the list and which expect manual entry varies by client and by version, so verify yours rather than assuming either.
What exactly should <code>/v1/models</code> return?
A JSON object with object set to list and a data array whose entries each carry an id plus object, created and owned_by. The id is the string you send as model in a completion request. If the body you get back does not start with that shape, you are not looking at the endpoint's model list — check the URL and what answered.
Is the model id the same as the one in the page URL?
Not necessarily, and this is a real 404 source. Ids get rewritten for the address bar: in our own catalogue, 15 of the tracked ids differ from their URL slug — for example claude-haiku-4-5-20251001 is written claude-haiku-4-5 in a URL. Copy ids out of the /v1/models response or out of a rate table that quotes the raw id, never out of a browser address bar.
My model works but the client truncates long files. Why?
Because clients guess the context window from the model name, and a name they do not recognise gets a conservative default. Serve a model under a custom id and the client may assume a small window and trim your prompt silently — no error, just missing context. Where the client exposes a model configuration section, enter the window and the maximum output tokens explicitly instead of leaving them to be inferred.
The list came back enormous and the picker is unusable.
That is a different problem with the same symptom. A gateway catalogue can run to hundreds of entries — ours exposes 839 — and a picker rendering all of them is practically unusable. Prefer clients that let you type an id, or that let you pin a shortlist. A huge list is evidence the endpoint is working, not that it is broken.
Should I ever hardcode the model id?
Yes, for a client that does not fetch the list, and it is the more predictable choice in production either way. Put it in configuration rather than inline so that changing models is a config change. The failure to avoid is hardcoding a string you copied from somewhere other than the endpoint's own list.