Troubleshooting · Models

model not found / model_not_found — one message, three unrelated causes

30-SECOND VERSION
  1. Call /v1/models and read the real list before checking spelling — three causes return the same message and only one is a typo.
  2. The id is in the list and it still fails = your account isn't entitled to it (semantically a 403; many endpoints answer 404 so the catalogue can't be enumerated).
  3. Short alias works, full name 404s = normal behaviour on an alias-mapped endpoint — but an alias isn't a stable identifier, so results stop being reproducible.

This error does not prove the model name is wrong. It equally often means your account isn't entitled to that model, or that the endpoint rewrites model names and yours isn't in its table. Trying different spellings fixes exactly one of the three. One request tells you which one you have: ask the endpoint what it actually serves.

What you're seeing

any of these
API Error: 404 {"type":"error","error":{"type":"not_found_error","message":"model: <model-id>"}}

{"error":{"message":"The model `<model-id>` does not exist or you do not have access to it.","type":"invalid_request_error","param":null,"code":"model_not_found"}}

404 model not found

{"type":"error","error":{"type":"not_found_error","message":"Not Found"}}

The second one is the honest one — it names both possibilities in the same sentence and declines to say which. Treat the other two as saying the same thing less clearly. The "or you do not have access to it" half is not filler: it points at 403 Forbidden territory wearing a 404's clothes.

The fourth is an impostor. It contains no model name, and that is the tell: a not_found_error with no model in it is usually the route being missing, not the model. See step 4.

Three causes, one message

What is actually trueHow it behavesWho can fix it
A — Name the endpoint doesn't serveThe ID is well-formed, just not in this endpoint's catalogFails instantly, every time. Other models on the same endpoint workYou
B — No entitlementThe model exists and is served; this account or key isn't allowed to reach itFails only for that model or that tier. Often starts after a plan, region, or account change you didn't makeThe account owner or the endpoint operator
C — Alias mappingThe endpoint maps names to its own models, and your fully-qualified ID isn't a key in that mapShort name works, full name 404s — on the same endpoint, in the same minuteYou (use what the catalog lists)

Cause B is the one the wording hides. Semantically it's an authorization result — the correct answer would be a 403 with permission_error, which is a different page: 403 Forbidden. Many endpoints return 404 instead, deliberately, so that a probing request can't be used to enumerate which models exist. The side effect is that you spend an hour on spelling for a problem that has nothing to do with spelling.

Why the short name works and the full name doesn't

A compatible endpoint is under no obligation to serve the same catalog as the upstream provider, and most don't. What they publish instead is an alias table: short, undated tier names — haiku, sonnet, opus — each routing to whichever model that endpoint has selected for that tier. Some also substitute a default when a request names no model at all.

Two things follow, and both look like bugs when you don't expect them:

  • The dated, fully-qualified ID you copied out of provider documentation may simply not be a key in that table. It is a perfectly valid model ID that this particular endpoint has no entry for. 404 on the full name, 200 on the short name, is the designed behaviour of an alias-mapped endpoint — not a fault, and not something a different spelling will get around.
  • A short alias is not a stable identifier. What sonnet resolves to can change on the endpoint's side, with no change to your config. Convenient today; unreproducible when you come back to a result in three months and can't say which model produced it.

For the same reason this page does not reproduce anyone's alias table, and you shouldn't trust one you find written down elsewhere. What a tier name resolves to, and whether a request that names no model gets a substitute at all, are properties of the endpoint on the day you ask it. Which is why the next section starts with reading the catalog rather than guessing at names.

Check in this order

1 · Ask the endpoint what it actually serves#

This is the step that separates all three causes. Do it first.

terminal
# the authoritative list — whatever this prints is what you are allowed to ask for
curl -s https://api.9coding.com/v1/models \
  -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
  -H "anthropic-version: 2023-06-01" | jq -r '.data[].id'

If the endpoint authenticates on x-api-key instead:

terminal
curl -s https://api.9coding.com/v1/models \
  -H "x-api-key: sk-9c-xxxxxxxx" \
  -H "anthropic-version: 2023-06-01"

The jq filter above assumes the common response shape. If it prints nothing, drop the pipe and read the raw JSON — you are after the identifiers, whatever key they arrive under.

Inside Claude Code, the /model picker lists the same catalog — either source is authoritative, and both beat retyping an ID from memory. Copy the id; don't retype it.

Read the output this way:

  • Your exact ID is in the list → this is not a naming problem. Go to step 6 (cause B).
  • The list contains only short names → alias mapping. Use a listed name (cause C).
  • Your ID is absent but near-identical dated IDs are present → naming (cause A). Copy one from the list; don't retype it.
  • /v1/models itself 404s → the endpoint publishes no catalog. Continue to step 2, and note what that costs you — see When it isn't your problem.

2 · Compare the string character by character#

The eye is bad at this. The shell isn't:

terminal
printf '%s' "$ANTHROPIC_MODEL" | od -c | tail -3

What actually goes wrong, in rough order of frequency: a provider prefix the endpoint doesn't want (anthropic/, openai/) or one it requires and you omitted; a region or deployment prefix; a date suffix that's missing, extra, or from a different snapshot; . where the ID uses -; capitalisation; and a trailing newline picked up by copy-paste — which od -c will show as \n at the end.

3 · Find out where the name is even coming from#

You may be editing one source while the client reads another.

terminal
env | grep -i -E 'anthropic|model'

Check every hit, not just the one you remember setting — the exact variable names, including the ones that pin a smaller model for background work, have moved between releases, so match what you find against the client version you're actually running rather than against a blog post. Then check the client's own config file and any per-project settings — a model pinned months ago is a classic: it works for as long as that snapshot is served and 404s on the day it isn't, with nothing changed on your side.

Restart the client afterwards. Long-running processes keep the environment they started with.

4 · Confirm it's the model and not the route#

terminal
curl -sS -o /dev/null -w '%{http_code}\n' https://api.9coding.com/v1/models
curl -sS -o /dev/null -w '%{http_code}\n' https://api.9coding.com/v1/messages

If /v1/models 404s too while the host resolves fine, suspect the base URL rather than the model — most often a base URL that already ends in /v1, so the client's own path makes it /v1/v1/models. That returns a Not Found with no model name in it, which is exactly the fourth message above.

terminal
echo "$ANTHROPIC_BASE_URL"   # exactly https://api.9coding.com — no trailing slash, no /v1

5 · Two minimal requests, exact ID then alias#

terminal
# <model-id>: copy an id from https://api.9coding.com/v1/models
curl -sS -w '\n%{http_code}\n' https://api.9coding.com/v1/messages \
  -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
  -H "content-type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{"model":"<model-id>","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'

Run it again with a short tier name in place of <model-id>, then read the pair:

  • Full ID 404s, short name 200s → cause C. The endpoint maps names; use a name from step 1's list.
  • Both 404 → cause A or B. Step 1's list decides which.
  • Both 200 from curl, but the client still fails → the client isn't sending the model you think it is. Go back to step 3.
  • Both fail with 401 → not a model problem at all. See 401 Unauthorized.

6 · If the name is right and listed, it's entitlement — ask one question#

Rotating your key will not help. A key that authenticates successfully and still can't reach one specific model is an authorization outcome delivered in a 404's wording, and a new key has the same authorization. This is the same distinction the 403 Forbidden page draws between "who are you" and "what are you allowed to reach".

Ask the operator one precise question instead of three vague ones:

Is <model-id> enabled for this key? If not, which served model is the intended equivalent?

The second half matters more than the first — on an alias-mapped endpoint the answer is often a name you would never have guessed by trying spellings.

When it isn't your problem

  • Your ID is in /v1/models and requests to it still 404. The catalog and the router disagree. Nothing on your machine can reconcile them.
  • It worked yesterday and you changed nothing. Either a snapshot reached the end of its life upstream, or the endpoint's alias table moved under you. Both are events with a date; both should appear in a changelog.
  • Other models on the same endpoint work fine. That rules out credentials, base URL, and network in one observation — you are looking at cause A, B, or C, never at a connectivity problem.
  • A short name works and the full name doesn't. Described above. Expected on an alias-mapped endpoint.

Check the operator's status page and changelog before you spend another hour rewriting model names — ours is at status. An endpoint that publishes no model list is an endpoint where these three causes cannot be told apart. You're left guessing spellings for a problem that may not be a spelling at all. That is a property of the endpoint, not of your setup, and it's worth knowing before you build on it.

When reporting it, include the request id — 9Coding error responses carry one in the form (request id: 2026...). That id plus the timestamp, the model name and the full error text lets the exact call be traced. A report that only says it doesn't work cannot be investigated.

404 or 403 — read it as "not available to you"

not_found_error here means not found for this request, which folds together "doesn't exist", "isn't served here" and "isn't yours". Don't read it as proof the model doesn't exist. If you get an explicit 403 with permission_error, that's the same cause B, just stated plainly — see 403 Forbidden.

Related