DEV Community

Multigrid
Multigrid

Posted on • Originally published at multigrid.ai

“Model Not Found” and the Naming Traps Behind It

model_not_found — usually an HTTP 404 with a message of the form the model X does not exist or you do not have access to it — conflates two completely different states, and the wording admits it. Either the name is wrong, or the name is right and your account cannot see it. Everything below is about telling those apart.

What the error looks like, and the 403 twin

HTTP/1.1 404 Not Found
{
  "error": {
    "message": "The model 'gpt-4o-2024-01-01' does not exist or you do not
                have access to it.",
    "type": "invalid_request_error",
    "param": "model",
    "code": "model_not_found"
  }
}
Enter fullscreen mode Exit fullscreen mode

The deliberate ambiguity in that sentence is an anti-enumeration measure: if a 404 meant “no such model” and a 403 meant “exists but not for you”, anyone could map a provider’s unreleased catalogue with a script. Some providers resolve it anyway and return 403 with a permission_denied code when the model exists and you lack entitlement; if yours does, you have been handed the answer and can skip to the entitlement branch.

The decision procedure

Follow this in order. Each step is cheap and each one eliminates a branch.

  1. List the models your key can actually see. Nearly every provider exposes a models endpoint, and it is scoped to your credential, which makes it the authoritative answer to “can this key use that name”.

    curl -sS https://api.example-provider.com/v1/models \
      -H "Authorization: Bearer $PROVIDER_API_KEY" \
      | jq -r '.data[].id' | sort | grep -i <fragment>
    

    If the exact string you sent appears in that list, the name is fine and the problem is elsewhere — go to step 4. If a very similar string appears, go to step 2. If nothing similar appears, go to step 3.

  2. Diff your string against the near match, character by character. Print repr() of the model string your code sends rather than reading the source. Hyphen versus underscore, a dot in the version, a trailing space from a config file, a smart quote from a document, uppercase where the provider is case-sensitive: all of these produce this error and none of them are visible when you read the line.

  3. Check the endpoint, then the region, then the entitlement. A correct name against the wrong base_url is a 404, and an environment variable overriding your base URL is invisible in the code. Some deployments only serve a subset of the catalogue per region. And some models require explicit enablement on the account, a tier, or an accepted licence — the console will say so where the API will not.

  4. Check the API surface. A name can be valid on one endpoint and unknown on another: embedding models are not chat models, image models are not either, and a completion-only model posted to a chat endpoint can 404 rather than 400. Confirm you are calling the endpoint that model belongs to.

  5. Check whether it was retired. If the name worked before and stopped, and it carries a date suffix, it was probably sunset. See the deprecation section.

The five naming traps

Trap Description
Alias versus snapshot A friendly name that always points at the current release, versus a dated identifier pinned to one build. Aliases never 404 and silently change behaviour; snapshots never change behaviour and eventually 404. Neither is free.
Deployment names On enterprise deployments, the string in the request is the name YOU gave the deployment, not the vendor's model name. Sending the vendor's name to a deployment-addressed endpoint 404s every time, and the fix is to look up what you called it.
Vendor prefixes Gateways and multi-provider clients namespace models, so the same model is 'gpt-4o' in one client and 'openai/gpt-4o' in another. Moving code between the two produces this error in both directions.
Size and variant suffixes -mini, -turbo, -instruct, -latest, -preview and quantisation suffixes on open weights. The base name usually exists; the variant you assumed may not.
Local model paths With a local runtime the 'model' field is a repository ID or a file path, and a 404-equivalent means the weights were never fetched or the path is wrong relative to the server's working directory, not the client's.

The alias-versus-snapshot choice is the one worth deciding deliberately rather than inheriting. An alias means you will never see this error and will one day see output that changed with no deploy. A pinned snapshot means the opposite: stable behaviour, and a 404 on a date you did not have in your calendar. Pinning plus a monitored deprecation feed is the combination that fails loudly and on your schedule; there is more in silent model updates.

When the model really is gone

Retirement is usually announced weeks ahead on a deprecations page and almost never in the API response, which is why it lands as a surprise. The response after the sunset date is a plain 404 — there is rarely a special code meaning “retired”, and there is rarely a pointer to the successor.

Do not repoint at the replacement and ship. A successor model is a different model: it will have different formatting habits, different refusal behaviour, different tool-calling reliability and different pricing. Run your evaluation set against it first, because the alternative is discovering the differences from user reports. If you do not have an evaluation set, this is the incident that justifies building one — a small golden set of thirty real cases is enough to catch the changes that matter.

Deprecation timelines are the fastest-moving thing on this page. Treat any specific date you read anywhere, including here, as needing confirmation against the provider’s current deprecations page.

Not being surprised next time

  • Resolve model names from configuration, in one place. A model identifier scattered across nineteen call sites is nineteen places to edit under time pressure. One module that maps a role — SUMMARISER, CLASSIFIER — to a concrete identifier makes the change a one-line diff.
  • Validate the names at start-up. Fetch the models list when the process boots and fail fast if a configured name is absent. A container that refuses to start is infinitely easier to diagnose than one that starts and 404s on the first user request at three in the morning.
  • Log the model field from the response, not the request. Providers echo back what they actually served, and for an alias that is the resolved snapshot. It costs one column and it is the evidence that settles most arguments about whether anything changed.

If several models are addressed through one client, the prefix and entitlement traps multiply. Multigrid exposes one catalogue of identifiers across providers, so a model name is resolved and validated in one place rather than per SDK, and a name that has been retired upstream fails at the gateway rather than in a request handler.

Related

Top comments (0)