Runbook 09 · Cloudflare Workers AI

Workers AI retires 24 models on 2026-11-20: the 410 that is not a 404, and the 5028 a retry policy drops

Platform: Cloudflare Workers AI, through the AI binding, the REST /ai/run endpoint, the OpenAI-compatible endpoint, and any AI Gateway configuration that stores a model name. Change: Scheduled change — effective 2026-11-20. The changelog published on 2026-11-05 lists 24 models (16 LLMs and 8 other models) that stop returning responses on that date; requests to them fail with HTTP 410 Gone and error code 5028. The date was originally 2026-11-15 and moved to 2026-11-20. Pre-change edition: reviewed against the published changelog on 2026-11-08. At review time the change had not taken effect, and nothing on this page was run against a live account. Detection chapter reviewed: 2026-11-08. The paid chapter is in verification; the status notice in the appendix stays until that finishes.

What changed

This is a catalog change, not a capacity or configuration change. Every ID below keeps working until 2026-11-20 and then fails permanently. No retry, quota change or gateway setting changes what a 410 means here, because the model is removed from the catalog rather than overloaded. Each item can be checked against the changelog and the model pages linked in the appendix.

  • 24 models retire on 2026-11-20 (changelog dated 2026-11-05). The post says apps must remove references to the listed models “before November 20, 2026”, and that the date was moved from November 15. The list is 16 LLM IDs and 8 other models: text-to-speech, speech-to-text, image generation, vision and one embedding model. The full catalog is the only place that shows what remains.
  • The failure shape is HTTP 410 with error code 5028. The changelog states that requests “will fail with an HTTP 410 Gone error and error code 5028”. The Workers AI errors table (last updated 2026-11-06) names the row Model deprecated, maps it to 410, and gives the message template ${model} was deprecated on ${date}. See the model catalog for alternatives. A 410 is a permanent answer: the same request will not start working later.
  • Three similar-looking codes, three different bugs. 410 + 5028 means the model is retired. 404 + 3042 means the model name is invalid. 400 + 5007 means no such model or task, phrased as No such model ${model} or task. A typo, a wrong prefix and a retirement can all look like “the model call failed” if the code reads only the status line.
  • The catalog already marks the retirement before it happens. Each deprecated model page carries a Planned deprecation badge and a Planned Deprecation | 11/20/2026 row while the model still answers. For example, @cf/meta/llama-3.2-1b-instruct and @cf/deepseek-ai/deepseek-v4-pro-0813 both show that row when reviewed on 2026-11-08. This is the cheapest way to confirm a single ID without sending a request.
  • The LLM replacements are category recommendations, not a one-to-one map. The changelog recommends @cf/deepseek-ai/deepseek-v4.1-flash-1017 and @cf/zai-org/glm-5.3 for frontier needs, and @cf/google/gemma-4-26b-a4b-it and @cf/qwen/qwen3.8-27b for smaller needs. It does not say which old LLM maps to which new one, so the choice is a workload decision, not a rename. The eight non-LLM models do have explicit replacements, such as @cf/openai/whisper-tiny-en to @cf/deepgram/nova-3 and @cf/bytedance/stable-diffusion-xl-lightning to @cf/black-forest-labs/flux-2-klein-9b.
  • The replacements differ in context, price, plan and defaults. Context windows run from 256,000 to 1,048,576 tokens, and two of the four recommended LLMs require the Workers Paid plan. The four also publish different reasoning-effort defaults, so an identical prompt can produce a different latency and token bill after a rename. The table below is copied from the model and pricing pages reviewed on 2026-11-08.
  • Some workloads have no drop-in replacement at review time. Three deprecated LLMs are LoRA base models, and their replacement is a base model rather than the adapter you fine-tuned. The embedding model @cf/google/embeddinggemma-300m is mapped to @cf/google/embeddinggemma-2 with the note “available soon”. A team that fine-tuned or built an index on those models has a different problem from a team that only calls a chat model.
Recommended LLM replacementContext windowUnit price (input / output)PlanReasoning defaults published
@cf/deepseek-ai/deepseek-v4.1-flash-10171,048,576$0.30 / $1.20 per MPaid access requirednone, low, high (default), max
@cf/zai-org/glm-5.31,048,576$1.40 / $4.40 per MPaid access requiredlow, high, max (default)
@cf/google/gemma-4-26b-a4b-it256,000$0.10 / $0.30 per MNot on the paid-only listReasoning: Yes, no levels published
@cf/qwen/qwen3.8-27b262,144$0.45 / $3.20 per MNot on the paid-only listlow, medium, xhigh (default)

For scale: the deprecated @cf/meta/llama-3.2-1b-instruct lists a 60,000-token context at $0.027 / $0.201 per million tokens, and the deprecated @cf/deepseek-ai/deepseek-v4-pro-0813 lists 1,048,576 tokens at $1.32 / $3.96. Replacing a 1M-context model with a 256k-context model, or a cheap model with a paid-only one, is a product decision that a global search-and-replace hides. Cached-input prices are omitted here; read them on the pricing page before you size the bill.

Detect: are you affected?

Three read-only checks. Check 1 finds every copy of a retiring model ID in the repository. Check 2 proves what one call returns before and after the effective date. Check 3 confirms a single ID against the catalog metadata. Run them before changing a model name, a fallback list or a gateway config.

Check 1 — inventory every hardcoded model ID

The changelog is also published as Markdown, so the list can be extracted instead of retyped. The sed range picks only the deprecated-models section: the LLM block and the “Other models” block, where the first backticked ID on each line is the retiring model. Expect exactly 24 IDs.

# 1. Extract the retiring IDs from the published changelog.
curl -sS https://developers.cloudflare.com/changelog/post/2026-11-05-planned-model-deprecations/index.md \
  -o /tmp/retired.md
sed -n '/#### Models deprecated on November 20, 2026/,/For the full list of available models/p' /tmp/retired.md \
  | grep -E '^- `@cf/' \
  | sed -E 's/^- `([^`]+)`.*/\1/' \
  | sort -u > /tmp/retired-models.txt
wc -l /tmp/retired-models.txt

# 2. Find every file that still contains one of those exact IDs.
grep -RIlF -f /tmp/retired-models.txt . \
  --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=.wrangler

# 3. Print the lines, so runtime copies can be separated from documentation copies.
grep -RInF -f /tmp/retired-models.txt . \
  --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=.wrangler
OutputMeaningNext step
24The extraction matched the published list exactly.Use /tmp/retired-models.txt for the scan. Continue.
any other countThe page structure changed, or the range matched another section.Do not trust the scan. Read the changelog list by eye and build the file manually.
no file path printedNo hardcoded ID in this repository. The name can still live in the dashboard, an AI Gateway config, a mobile client, or a different repository.Check those surfaces, then stop: this change does not affect you.
hits under src/, app/, functions/, worker/Runtime copies of the ID: front-end picker, route table, fallback array, test fixture, prompt template.Affected. Change every runtime copy; continue to check 2.
hits only under docs/, vendor/, or a quoted changelogNot runtime. Quoted documentation is not a model reference.Ignore those hits, but still check the dashboard and gateway.
one hit in a built bundle, none in sourceThe build embeds the ID, or the source uses a different spelling.Change the source and rebuild; do not patch the bundle.

Five places hold a model name in a typical AI site, and a repository scan only finds four of them: the front-end model picker, the backend route or environment variable, the evaluation and benchmark scripts, the fallback chain, and the AI Gateway model or alias field. The gateway one is configuration, not code: if the gateway was created in the dashboard, its model and fallback entries live only there, and a clean grep proves nothing about them.

Check 2 — probe one retiring ID against a control and a fake

The probe is deliberately three requests: one retiring ID from the list, one name that never existed, and one control model that is not on the list. The comparison is what distinguishes a retirement from a typo and from a credential problem. Set CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN first and keep the payload tiny; this command is read-only with respect to your application.

BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai/run"
probe () {
  printf '\n== %s\n' "$1"
  curl -sS -o /tmp/ai-probe.json -w 'HTTP %{http_code}\n' \
    -X POST "$BASE/$1" \
    -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"messages":[{"role":"user","content":"reply with the single word ok"}]}'
  head -c 400 /tmp/ai-probe.json; echo
}
probe "@cf/meta/llama-3.2-1b-instruct"   # one ID from /tmp/retired-models.txt
probe "@cf/definitely-not-a-model"      # a name that never existed
probe "@cf/meta/llama-3.1-8b-instruct"  # a control that is not on the list
OutputMeaningNext step
retiring ID: 200 (before 2026-11-20)The model still answers. This is the expected pre-change result and the reason the probe alone cannot decide.Affected by the schedule, not broken yet. Migrate before the effective date.
retiring ID: 410 + 5028 (on or after 2026-11-20)Retirement is live. The body carries Model deprecated and names the model and date.Read the code, not just the status. Go to failure modes 01 and 02.
fake ID: 400 + 5007, or 404 + 3042The model name is wrong. 5007 is No such model ${model} or task; 3042 is The model name is invalid.Not a retirement. Fix the ID or the prefix, then probe again.
retiring ID: 403 + 5035This model requires a Workers Paid plan. The request reached the model catalog; billing blocked it.Plan problem, not a retirement. See failure mode 07.
control ID: 200Token, account and endpoint path work, so a 410 on the retiring ID is about the model.Trust the comparison. Continue.
control ID: 401 or 403 without 5035Credential or account problem; every probe result above is unreliable.Fix auth first. This runbook is not the right one for that failure.

Check 3 — confirm one ID in the catalog metadata

Each model page is published as Markdown too. A deprecated model shows a planned deprecation row while it still answers, so this works before the effective date and costs one request per ID.

curl -sS https://developers.cloudflare.com/workers-ai/models/llama-3.2-1b-instruct/index.md \
  | sed -n '/Planned Deprecation/p'
OutputMeaningNext step
| Planned Deprecation | 11/20/2026 |The catalog marks this ID for retirement on the effective date.Treat it as affected even if the probe returned 200.
no outputNo planned-deprecation row on that page at the review date.That ID is not on this list. Stop for that ID, and check the rest of your inventory.
404, or a page for a different modelThe slug changed or the model is no longer in the catalog.Look it up in the catalog before concluding anything.

Decision tree

inventory scan (check 1)
│
├─ no runtime copy ──► check dashboard + AI Gateway config
│                      ├─ clean ──► not affected: stop, do not buy
│                      └─ model present ──► affected: continue
│
└─ runtime copy ──► probe that ID (check 2)
                     │
                     ├─ 200 before 2026-11-20 ──► affected on schedule
                     │                            migrate before the date
                     ├─ 410 + 5028 ──► retirement live ──► FM-01, FM-02
                     ├─ 400 + 5007 / 404 + 3042 ──► wrong ID, not retirement
                     ├─ 403 + 5035 ──► replacement needs Paid ──► FM-07
                     └─ 401 / 403 (no 5035) ──► auth problem, out of scope

If checks 1 and 3 find nothing and the dashboard and gateway hold no retiring ID, stop: this runbook is not the right one, and buying it will not help. If the scan finds copies only in documentation, the same applies. The paid material starts once a runtime copy exists and the question is how to move it without changing behaviour silently.

Failure modes

Seven failures, each with the trigger, the documented error and the fix. Two are visible here.

01 — The 410 becomes “document not found”
The errors table defines 5028 as Model deprecated with HTTP 410, and the message template is ${model} was deprecated on ${date}. See the model catalog for alternatives. Clients that map any 4xx to “missing resource” (inferred client behaviour) turn that into document not found, chat not found or conversation expired in the UI, so the operator debugs content storage while the model is simply gone. The three codes are not interchangeable: 410 + 5028 is a retired model, 404 + 3042 is an invalid model name, and 400 + 5007 is No such model ${model} or task. Read the internal code from the response body before choosing a user-facing message, and name the model in the error you show.
02 — The retry classifier that drops the request
Most retry helpers are written as “retry 5xx and timeouts, drop other 4xx” (inferred: depends on the client library). A 410 is a 4xx, so the request enters the permanent-failure branch and disappears: no retry, no dead letter, no metric, often no log line. The symptom is a batch or evaluation that reports success with fewer records than it started with, and support cannot find the failed item because nothing recorded it. A 410 is terminal, but it is not ignorable: it must be logged with the model ID and the deprecation date, counted, and parked where a human can replay it after the model is replaced. Any batch that can silently shrink is a reporting bug on top of a catalog change.
The remaining five, and the migration, are in the paid bundle
Failure modes 03–07: a fallback chain where the primary was updated and the standby still points at a retiring model, so the failure appears only under load; an AI Gateway model or alias field that drifted from the code, making the logs name a different model than the request did; a replacement with a smaller context window, a different price and a different reasoning default, so the same prompt gets slower and more expensive; an evaluation harness that swaps the default model silently and produces numbers that are no longer comparable; and replacements that are not drop-in at all — a paid-only model behind a Free-plan account, a LoRA base model whose adapter cannot move, and an embedding replacement that was “available soon” at review time. The migration checklist is split by caller (AI binding, REST and OpenAI-compatible clients, AI Gateway, front-end picker, eval harness, fine-tunes and indexes), with a rollback plan and a 24-hour watch list. It arrives as Markdown files, one per chapter, so your agent can read them directly. A refund revokes access.

Migration: what the paid chapter covers

The change is one date, but the work is per caller: each surface that stores a model name fails at a different moment, and some of them fail silently. The runbook splits the migration this way:

Caller / componentWhat breaksWhat the runbook has you change
Worker using the AI bindingThe ID is in code; the call throws after the effective date, and a streaming call can fail after the response has startedReplace the ID, log the code and the model, and re-test the streaming path with the replacement before removing the old name
REST /ai/run clientThe path contains the model ID; a 410 can be mistaken for a bad URL or a missing documentPin the new ID in one constant, assert the status-code-to-code mapping, and surface 5028 as a model-retirement error
OpenAI-compatible clientThe model ID is a request field, often defaulted by config or by a librarySet the model explicitly at the call site, remove the default, and fail the build if a retiring ID is configured
AI Gateway model, alias and fallbackA second copy of the name lives outside the repository; the gateway can serve a different model than the code asks forInventory gateways and dynamic routes, update every model and fallback entry, and compare the logged model with the requested model
Front-end model pickerThe old ID stays in a dropdown and produces a 410 only for the users who pick itShip the new list, keep one legacy option only if it maps to a replacement, and test the empty and error states
Evaluation / benchmark harnessA default model or a per-file model list swaps silently; scores are compared across two modelsStamp every result with the exact model ID, stop a run on a 410, and re-baseline instead of comparing to the old scores
Fine-tunes and LoRA adaptersThe adapter is tied to a deprecated base model; the replacement is a base model, not the adapterDecide per adapter: retrain on a supported base, or move that workload off Workers AI
Embedding pipelines and indexesThe embedding replacement was “available soon” at review time; a different embedding changes the vectorsFreeze the old index read-only, verify the replacement dimension and output before writing, and re-embed as a batch job

Appendix

Verification status
This is a pre-change edition. The detection chapter above is reviewed against the sources linked in this appendix on 2026-11-08 and is complete; it describes a scheduled change, not an observed one. The paid chapter (failure modes 03–07 and the migration checklist) is still in re-verification, and no part of this page was re-run on a live account at review time, so treat the paid material as provisional until that finishes. Price and structure are final. A 7-day refund applies from the day of purchase — see the refund policy.
For agents

Need a runbook that does not exist yet?

Runbooks are written by an AI from requests. Submit the platform change, the versions affected and public evidence; accepted requests become the next runbook.

Request one
Agents: POST /api/requests with JSON, or read the instructions at /requests.md. Accepted and published requests are listed at /api/requests. Humans: email [email protected].

Got a 410 where a model used to answer?

Send the model ID, the status line and the internal code. If it is one of the seven failures in this runbook, the fix is one chapter away.