Claude 3 → Claude 4 → Claude 5 migration — retired model IDs and API changes (2026) — Fix Guide (2026) | AI Error Hub
Home Providers Claude Model migration
Claude Model Lifecycle · Migration Severity: High HTTP 404

Claude model migration — retired IDs, new versions, and behaviour deltas

Claude 3 is retired. Claude 4.x is stable production. Claude Sonnet/Opus 5 and Fable 5 are the newest. Each generation has ID changes and subtle behaviour deltas — this page maps every migration path.

By Sana K. · Cloud AI Reliability Engineer Published 2026-07-26 Verified 2026-07-26

Quick fix (TL;DR)

Resolution: Retired: all Claude 3 IDs (claude-3-opus-20240229, claude-3-5-sonnet-20240620, etc.). Current stable: Claude 4.6 and 4.7 family plus Sonnet 5, Haiku 4.5, and Fable 5. Retired IDs return 404 or auto-substitute to a successor with different behaviour. Fix by (a) mapping every hardcoded model ID in your codebase, (b) testing behaviour on the target model against your prompt suite, and (c) using aliases (claude-sonnet-5) where you want auto-updates and pinned dates where you do not.

Real error messages you'll see

These are the exact strings returned by the Claude API service and its SDKs when this error occurs. Copy-paste-searching any of them should land on this page.

404 — retired Claude 3 model
anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-3-opus-20240229 not found.'}}
Deprecation warning header
HTTP/1.1 200 OK
anthropic-deprecation-notice: "claude-3-5-sonnet-20241022 is deprecated and will be retired on YYYY-MM-DD. Migrate to claude-sonnet-5."
Behaviour regression — same prompt, worse output
# Before (claude-3-5-sonnet-20241022): tight, terse answers
# After (claude-sonnet-5): more verbose, sometimes over-explains
# Symptom: latency and token cost up 30% on the same workload.

Reference

Migration map — retired → current (2026)

Retired IDRecommended successorNotes
claude-3-opus-20240229claude-opus-4-7Massive capability jump, similar cost
claude-3-sonnet-20240229claude-sonnet-5Faster + smarter
claude-3-haiku-20240307claude-haiku-4-5-20251001Cheaper + smarter
claude-3-5-sonnet-20240620claude-sonnet-5Similar tier, better
claude-3-5-sonnet-20241022claude-sonnet-5Successor generation
claude-3-5-haiku-20241022claude-haiku-4-5-20251001Successor generation
claude-3-7-sonnet-20250219claude-sonnet-5 or claude-opus-4-7Depends on task

Current model families (2026)

FamilyBest forContextNotable features
Claude Opus 4.6/4.7Complex reasoning, long horizon coding, agents200KExtended thinking, best quality
Claude Sonnet 4.6/5General purpose, cost-sensitive production200K / 1MFast + smart, extended thinking
Claude Haiku 4.5High-volume classification, quick tasks200KVery fast, cheap
Claude Fable 5Creative writing, story generation200KLong-form narrative optimised

Root causes, ranked by frequency

Based on developer reports across Claude API forums, GitHub issues, and Anthropic community during 2025–2026.

  • 30%
    Retirement date reached with no migration. Team ignored deprecation warnings; day-zero 404 on production.
  • 18%
    Auto-substitution changes behaviour. Anthropic (or Bedrock/Vertex) silently maps the old ID to a newer one; quality shifts, tests fail.
  • 14%
    Hardcoded model IDs scattered across the codebase. Migration requires a code sweep; one straggler stays on the old ID.
  • 10%
    Model behaviour differs on same prompt. Newer model produces more verbose or differently-formatted output; downstream parsers break.
  • 8%
    Cost per request higher on the successor. New model priced higher per token even though it is smarter — total cost per task can be higher or lower.
  • 7%
    API version mismatch. Successor model may require a newer anthropic-version header.
  • 7%
    Provider platform lag. New Anthropic model available on the API but not yet on Bedrock/Vertex; team on Bedrock unable to migrate on the same schedule.
  • 6%
    Fine-tunes retire with the base model. Custom Claude fine-tunes (where available) retire when their base retires.

Fixes — copy-paste solutions

Fix #1

Audit every hardcoded model ID in the codebase

You cannot migrate what you cannot find.

Search the codebase for every model ID reference and consolidate to a single source of truth (constants module, env var). This is the necessary first step before behaviour testing.

audit_model_ids.sh
# Find every hardcoded Claude model ID in the repo
rg -n '"claude-[0-9a-z-]+"' \
   --type py --type ts --type js --type go --type rb --type yaml --type json

# Find bare model references (aliases without a version)
rg -n 'model["\s]*[:=]\s*["\']?claude' \
   --type py --type ts --type js

# Common patterns in configs
rg -n 'CLAUDE_MODEL\|ANTHROPIC_MODEL\|OPUS_MODEL\|SONNET_MODEL' \
   --type py --type ts --type js --type yaml --type env

# Once you have the list, consolidate to a single constants file
# constants/models.py:
#   OPUS   = "claude-opus-4-7"          # or a pinned @date if you need stability
#   SONNET = "claude-sonnet-5"
#   HAIKU  = "claude-haiku-4-5-20251001"
#   FABLE  = "claude-fable-5"

# Replace every hardcoded ID with the constant. Now migration is a one-file change.
For teams that need reproducibility (regulated industries, research), pin the @YYYYMMDD version and treat model upgrades as deliberate changes with sign-off. For agility, use unversioned aliases and let Anthropic move you.
Fix #2

Test the migration on a prompt suite before cutover

Behavioural regressions on the new model are the biggest migration risk.

Run your 20-100 most representative prompts through both the current and target models. Diff the outputs. Measure output length, latency, and any downstream parsing metrics.

migration_test.py
"""Run a prompt suite against old + new model and diff results."""
import json
import time
import anthropic
from pathlib import Path

client = anthropic.Anthropic()

OLD_MODEL = "claude-3-5-sonnet-20241022"   # currently in production
NEW_MODEL = "claude-sonnet-5"              # target

def run(model: str, prompt: dict):
    t0 = time.time()
    r = client.messages.create(
        model=model,
        max_tokens=prompt.get("max_tokens", 1000),
        system=prompt.get("system"),
        messages=prompt["messages"],
    )
    elapsed = time.time() - t0
    return {
        "model": model,
        "text": r.content[0].text if r.content else "",
        "output_tokens": r.usage.output_tokens,
        "input_tokens": r.usage.input_tokens,
        "elapsed_s": round(elapsed, 2),
        "stop_reason": r.stop_reason,
    }

# Load your prompt suite
prompts = json.loads(Path("prompt_suite.json").read_text())

results = []
for p in prompts:
    old = run(OLD_MODEL, p)
    new = run(NEW_MODEL, p)
    results.append({
        "name": p["name"],
        "old": old,
        "new": new,
        "diff": {
            "output_tokens_delta": new["output_tokens"] - old["output_tokens"],
            "elapsed_delta_s":    round(new["elapsed_s"] - old["elapsed_s"], 2),
            "same_stop_reason":   old["stop_reason"] == new["stop_reason"],
        },
    })

# Summarise
total_old = sum(r["old"]["output_tokens"] for r in results)
total_new = sum(r["new"]["output_tokens"] for r in results)
print(f"Output tokens: old={total_old}, new={total_new}, delta={total_new-total_old:+d}")
print(f"Avg latency: old={sum(r['old']['elapsed_s'] for r in results)/len(results):.2f}s, "
      f"new={sum(r['new']['elapsed_s'] for r in results)/len(results):.2f}s")

# Manually review the diff on outputs where old vs new differ substantially
Behavioural regressions are almost always about output length or formatting. If your downstream parser expects JSON in a specific shape, verify strict shape parity. New models sometimes add extra prose around the JSON.
Fix #3

Decide: pinned versions or aliases?

Fundamental strategy choice that shapes your migration cadence.

Pinned versions (claude-sonnet-5-20250601) mean the model behaviour never changes under you — but you must migrate deliberately. Aliases (claude-sonnet-5) mean Anthropic can update the underlying model — smoother but less predictable.

strategy_config.py
"""Strategy: pin versions in prod, use aliases in dev/staging."""
import os

# Environment-aware model selection
ENVIRONMENT = os.getenv("APP_ENV", "development")

MODEL_CONFIG = {
    "production": {
        # Pinned — behaviour stable, migration is a deliberate PR
        "opus":   "claude-opus-4-7-20250514",
        "sonnet": "claude-sonnet-5-20251015",
        "haiku":  "claude-haiku-4-5-20251001",
    },
    "staging": {
        # Alias — validates that a future rollout of the new version works
        "opus":   "claude-opus-4-7",
        "sonnet": "claude-sonnet-5",
        "haiku":  "claude-haiku-4-5",
    },
    "development": {
        # Alias — fastest to try new features
        "opus":   "claude-opus-4-7",
        "sonnet": "claude-sonnet-5",
        "haiku":  "claude-haiku-4-5",
    },
}

def model(tier: str) -> str:
    return MODEL_CONFIG[ENVIRONMENT][tier]

# Usage
response = client.messages.create(
    model=model("sonnet"),
    max_tokens=500,
    messages=[...],
)

# Migration path:
# 1) Run test suite against latest alias in staging weekly
# 2) When alias behaviour changes, decide: adopt or hold
# 3) On adopt, bump the pinned version in production and deploy
A common pattern: staging always tracks the alias; production pins. Weekly, diff staging metrics vs production to detect model updates that would affect you.

Prevention checklist

Ship these seven safeguards once and this error stops appearing in your logs.

  • Consolidate every Claude model ID to a constants module — no hardcoded IDs in application code.
  • Subscribe to Anthropic's deprecation announcements (blog + email) and add retirement dates to your calendar.
  • Run a monthly migration-test script even when no deprecation is announced — catches upstream behaviour drift.
  • For regulated / high-stability workloads, pin @YYYYMMDD versions and treat updates as deliberate changes.
  • For dev/staging environments, use aliases so future updates surface early.
  • Maintain a "supported models" registry per platform (Anthropic API, Bedrock, Vertex) — availability lags differ.
  • Include model migration in your quarterly platform-review meeting; deprecation surprises are avoidable.

Frequently asked questions

Retirement dates are announced with typically 6+ months of notice for GA models and shorter for previews. Actual retirement is graceful — the model returns a 404 rather than silently doing nothing. Some retired IDs auto-substitute to a successor.
Fable 5 is the creative-writing optimised variant in the Claude 5 family. Better at long-form narrative, less rigid formatting, more voice consistency across a piece. Not the right choice for factual or agentic tasks — use Sonnet or Opus for those.
Not always immediately. Anthropic rolls out newer versions via aliases after a stability period. If you need "always latest", use the alias. If you need "known behaviour", pin.
No — Anthropic API tends to have new models first, followed by Bedrock and Vertex within days to weeks. If a specific model is critical, check availability on your provider before committing to a migration date.
Dev → staging → production, with soak time at each level. On staging, run production-representative traffic through the new model for at least 3-5 days. Look for latency shifts, cost shifts, and downstream parser errors. Only then flip production.

Get the weekly AI-error digest

New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.