◉CLAUDE LABJP
●2.1.312 — Fixes Claude overwriting very large CLAUDE.md files and skills going missing after a Bash cd●API — The Messages API maximum request size grew from 32 MB to 128 MB (10/28)●SONNET 4.5 — Retires on the Claude API on 11/30, 29 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST — Opus 4.8 fast mode is deprecated and will be removed on 11/30. Migrate to Opus 5.5●NEW — I Put a Ten-Line Promise File at the To…●2.1.312 — Fixes Claude overwriting very large CLAUDE.md files and skills going missing after a Bash cd●API — The Messages API maximum request size grew from 32 MB to 128 MB (10/28)●SONNET 4.5 — Retires on the Claude API on 11/30, 29 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST — Opus 4.8 fast mode is deprecated and will be removed on 11/30. Migrate to Opus 5.5●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude.ai
◉ Claude.ai/2026-11-01Beginner

"Not Sooner Than November 24" Is Not a Retirement Date: Matching My Own Model IDs to the Table on a Sunday

How to read Active, Deprecated and Retired in Claude's model retirement table, and why a 'not sooner than' date is only a floor. Includes a 60-line Python script that counts the model IDs in your own files, with the output from my run.

model retirementSonnet 4.53Opus 4.5model-deprecationsmigration13Python29

On Sunday morning I made coffee and opened the official model table. Sonnet 4.5 retires this month, and I wanted to reread the table once before November got busy.

One row stopped me. claude-opus-4-5-20251101 had "Not sooner than November 24, 2026" in the retirement column. That is next month, and for a second my stomach dropped.

Then I looked at the neighboring columns. The state was Active, and the Deprecated column said N/A. No notice had gone out. The "not sooner than" date and the day a model actually stops turned out to be two different things.

This post sorts out that difference using the table as it stood on November 1. I also include a small script that counts model IDs in a folder and checks them against the table, along with what it printed when I ran it on my own working folders. I'm an indie developer, and I've lost track of which model names I typed into which helper script.

The four words in the table, and the rows that matter to me

The Model deprecations page sorts every model into one of four states.

StateWhat the docs say (paraphrased)What you do
ActiveFully supported and recommendedNothing. The date column is a floor, not a deadline
LegacyNo more updates, may be deprecated laterKeep a replacement in mind
DeprecatedStill works, not recommended; a replacement and a retirement date are setMove before the date. This date is a real deadline
RetiredUnavailable; requests failFix any leftover reference right now

From the table as of November 1, I picked the three rows that touch my own work.

Model IDStateHow to read the date
claude-sonnet-4-5-20250929DeprecatedRetires 2026-11-30 (replacement: claude-sonnet-5-5). 29 days left
claude-opus-4-5-20251101ActiveWill not retire before 2026-11-24. No notice yet
claude-haiku-4-5-20251001ActiveWill not retire before 2026-10-15. That date has passed, still no notice

All three look like "rows with a date." Only the top one is a deadline.

A "not sooner than" date is a floor, not a stop date

The reasoning comes from the Notifications section of the same page. For publicly released models, Anthropic says it gives at least 60 days' notice before retirement.

The Sonnet 4.5 row follows that promise exactly. The notice went out on September 30, the retirement date is November 30, and the gap is 61 days. The moment the state became Deprecated, the deadline existed.

Opus 4.5 still has N/A in the Deprecated column. Read literally, a model nobody has been notified about has no November 24 deadline. "Not sooner than" only says the model won't be switched off before that day.

Here is the line I drew for myself.

A date is a deadline only on a row that has become Deprecated. On an Active row, I treat the date as a signal to start watching.

Of course, the closer the floor gets, the likelier a notice becomes. So the Opus 4.5 row doesn't mean "ignore it." It means "know where it's referenced now, so that when the email arrives, I can move the same day." I handled the Haiku 4.5 row the same way, and I wrote up how to read a passed date with no notice in Haiku 4.5's October 15 Floor Has Passed, but No Stop Date Has Been Set.

One more caution sits at the top of the page. These dates apply to Anthropic-operated platforms: the Claude API, Claude Platform on AWS, the Anthropic-managed Google Cloud offering, and Microsoft Foundry. Amazon Bedrock and the Google-managed offering set their own retirement schedules, so the same model can have different dates. If you reach Claude through more than one route, you need to read two tables.

Counting my own model IDs in 60 lines

Once the table makes sense, the next question is where those IDs actually sit in my own files. My working folders hold a handful of chores I've handed to AI, and I no longer remember which model name went where.

So I wrote a script that scans text files in a folder, pulls out model IDs, and compares them with a hand-copied version of the retirement table. It skips node_modules and similar folders, and unreadable files simply don't count.

#!/usr/bin/env python3
"""フォルダ内のテキストファイルから Claude のモデル ID を拾い、退役表の状態と突き合わせる。"""
import re
import sys
from collections import Counter
from pathlib import Path
 
# 2026-11-01 時点の公式「Model deprecations」表を手で写したもの。更新日を必ず併記する
STATE = {
    "claude-sonnet-4-5-20250929": ("Deprecated", "2026-11-30 退役"),
    "claude-opus-4-5-20251101": ("Active", "2026-11-24 より早くは退役しない"),
    "claude-haiku-4-5-20251001": ("Active", "2026-10-15 より早くは退役しない(日付は経過)"),
    "claude-opus-4-6": ("Active", "2027-02-05 より早くは退役しない"),
    "claude-sonnet-4-6": ("Active", "2027-02-17 より早くは退役しない"),
    "claude-sonnet-4-20250514": ("Retired", "2026-06-15 退役済み"),
    "claude-opus-4-20250514": ("Retired", "2026-06-15 退役済み"),
    "claude-opus-4-1-20250805": ("Retired", "2026-08-05 退役済み"),
}
# 日付つきの形(-20250514)を先に試す。順序を逆にすると日付の先頭 1 桁だけ拾って別の ID に化ける
PATTERN = re.compile(r"claude-(?:opus|sonnet|haiku)-\d(?:-\d{8}|-\d(?:-\d{8})?)?")
TEXT_SUFFIX = {".py", ".js", ".ts", ".json", ".yaml", ".yml", ".toml", ".sh", ".md", ".txt", ".env"}
 
 
def scan(root: Path) -> Counter:
    found = Counter()
    for path in root.rglob("*"):
        if not path.is_file() or path.suffix not in TEXT_SUFFIX:
            continue
        if any(part in {"node_modules", ".git", ".next"} for part in path.parts):
            continue
        try:
            text = path.read_text(encoding="utf-8")
        except (UnicodeDecodeError, OSError):
            continue  # 読めないファイルは黙って飛ばす(件数には入らない)
        found.update(PATTERN.findall(text))
    return found
 
 
def main() -> None:
    root = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
    found = scan(root)
    print(f"{'モデル ID':36} {'件数':>4}  {'状態':10} 備考")
    for model, count in found.most_common():
        state, note = STATE.get(model, ("表に無い", "公式の表で確認"))
        print(f"{model:36} {count:>4}  {state:10} {note}")
 
 
if __name__ == "__main__":
    main()

Usage is one argument, the folder to inspect.

python3 model_id_state.py ~/my-scripts

I made two decisions up front. First, an ID that isn't in the table is printed as "not in table" instead of being dropped. Silence and "needs checking" must never look the same. Second, the state table carries a date. A hand-copied table starts going stale the moment you copy it.

My first run turned one ID into another

I ran the first version on two of my folders, and the output contained claude-opus-4-2 and claude-sonnet-4-2, four hits each. Neither ID exists.

The cause was the order of alternatives in the regex. For an ID like claude-opus-4-20250514, the "hyphen plus one digit" part grabbed the 2 at the start of the date, and the remaining seven digits had nowhere to go, so they were thrown away.

The fix was to try the dated form first.

# Before: grabs the first digit of the date and yields a different ID
re.compile(r"claude-(?:opus|sonnet|haiku)-\d(?:-\d)?(?:-\d{8})?")
# After: dated form first, then generation form
re.compile(r"claude-(?:opus|sonnet|haiku)-\d(?:-\d{8}|-\d(?:-\d{8})?)?")

After the fix, the same eight hits became claude-sonnet-4-20250514 and claude-opus-4-20250514, and both were classified as Retired. In other words, four references to each already-retired model were sitting in my notes, and the broken version had hidden them.

Reading the result: a big count is not a big risk

Here are the results for the two folders side by side.

FolderIDs foundWhat it told me
Instructions and scripts6 kinds, 1 hit each. No Deprecated or RetiredNo call site that hits the deadline
Research notes19 kinds. Sonnet 4.5 had 27 hits, Retired IDs had 10 in totalMentions in notes, not calls

In the notes folder, the soon-to-retire claude-sonnet-4-5-20250929 showed up 27 times. My heart jumped, but when I opened the files, every hit was a note I had written while researching the retirement. None of them sat in a request.

A big count is not a big risk. In a folder full of notes, the Deprecated count is not an alarm by itself. I decided to count executable files (.py, .js, .sh, configs) separately from reading material (.md, .txt).

The other thing I noticed was that the "not in table" bucket was the longest. Newer IDs such as claude-opus-4-8 aren't in my hand-copied table, which doesn't mean they're dangerous. Still, when most IDs are missing from the table, the comparison loses its point. Checking against the official table at the start of each month and adding rows is a chore I can't skip.

What to do this week

If I had to pick one action, it's this.

Search only your executable files for claude-sonnet-4-5-20250929. Zero hits means the November 30 deadline doesn't reach your files. If you find one, set aside time to compare outputs on the same inputs before switching to claude-sonnet-5-5.

How to count call sites from the Usage page CSV is covered in 43 Days Until Sonnet 4.5 Retires: I Counted Callers From the Usage CSV, Not From grep. A script that searches files and your actual usage history look in different places. Layering the two is, I think, what really shrinks the gaps.

I plan to run the script once more before the day ends and leave the result for Monday's me.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◉ Claude.ai2026-10-27
A Retirement Notice Landed This Morning: Three Screens to Open Before You Change Anything
An English deprecation email from Anthropic arrives and you don't write code. Here are three things to check first: whether it concerns you, what the date really means, and who to tell what.
◉ Claude.ai2026-10-15
Let Claude Draft the Quote's Wording, Keep the Numbers in Your Own File — A Small Source of Truth and a Check Script
How a freelancer can have Claude polish the wording of quotes and invoices while the amounts stay in a JSON file and get verified by a short Python script.
◉ Claude.ai2026-10-30
The Limit Rose to 128MB, but 176 Images at 17.5MB Still Stop: Count the Wall That Hits Before Bytes
The day the Messages API request limit became 128MB, I checked 176 images on my own disk. Bytes had plenty of room; the image-count cap hit first. Here is a pre-send gate and a chunking function.