GPT-5.5 leaves Codex on 14 October. Find every place you pinned it before a script breaks
A model ID that's hard-coded in a config, a workflow or a cron script stops working on the day of retirement. Here's a 40-line audit that finds each one, and the replacements OpenAI names.

On 14 October, a line like codex exec -m gpt-5.5 "Summarise open failures" in a nightly workflow stops being a working command. OpenAI’s Codex changelog
announced the retirement on 14 September: GPT-5.5 leaves ChatGPT, ChatGPT Work and Codex on all plans on 14 October 2026. Nobody gets paged for a deprecation notice from three weeks ago. They get paged when the job fails at 2am.
The fix for each pin is a one-word edit. The hard part is finding every pin, because model IDs get copied into places nobody revisits: a config file, a CI workflow, a shell script someone wrote for a Friday-afternoon chore. This piece gives you the replacements OpenAI names, a small script that lists every hard-coded reference under a directory, and a short list of places a file scan can’t reach.
What’s retiring, and what isn’t
The models page is specific about scope. GPT-5.5 retires from Codex, ChatGPT and ChatGPT Work. The OpenAI API is not affected.
That distinction decides who has work to do. If Codex signs in with a ChatGPT account, anything that selects gpt-5.5 has a deadline. If a script talks to the API directly with a key, this retirement doesn’t touch it. Mixed setups exist, so check which path each job uses before you decide a pin is harmless.
The same changelog lists a second casualty from the same day. GPT-5.3-Codex-Spark was deprecated on 14 September and is already gone from the desktop app, the CLI and the IDE extension. A script pinned to gpt-5.3-codex-spark is already broken, not about to be.
The replacements
OpenAI’s guidance depends on the plan. Plus, Pro, Business, Enterprise and Edu users signed in with ChatGPT should choose gpt-6-sol when it’s available to them. Free and Go users should choose gpt-6-luna in the desktop app when it’s available.
For complex coding and agentic work, the models page recommends gpt-6.1-sol when your account and client have it, and Luna for focused, repeatable tasks. That second recommendation is worth a thought for unattended jobs. A nightly summary or a changelog draft is a focused, repeatable task, and it may not need the model you use for a gnarly refactor. The only way to know is to compare outputs on your own inputs, as the Sol versus Astra piece
does for a coding task.
Setting the model takes one key. In config.toml:
model = "gpt-6.1-sol"
The docs say the desktop app, the CLI and the IDE extension share the same config.toml, so one edit covers all three. If model isn’t set at all, Codex uses a recommended model. On the command line, --model (or -m) overrides it for a single run, and /model switches inside a session.
That last point suggests a cheap defence. A pin that’s necessary is a pin you should be able to find in one place. A pin in twelve scripts is a future incident.
The audit script
The script below walks a directory, skips the folders that only hold generated files, and reports every line containing a retiring ID. It’s Python because it needs no install step.
#!/usr/bin/env python3
"""List every place a retiring model ID is pinned under a directory."""
import re
import sys
from pathlib import Path
RETIRING = {
"gpt-5.5": "gpt-6-sol (or gpt-6.1-sol)",
"gpt-5.3-codex-spark": "a model under 'recommended models'",
}
SKIP_DIRS = {".git", "node_modules", "bin", "obj", "dist"}
# \b would match inside "gpt-5.55", so require a non-ID character after the name.
PATTERNS = {m: re.compile(rf"(?<![\w.-]){re.escape(m)}(?![\w.-])") for m in RETIRING}
def scan(root: Path):
for path in root.rglob("*"):
if not path.is_file() or SKIP_DIRS & set(path.parts):
continue
try:
lines = path.read_text(encoding="utf-8").splitlines()
except (UnicodeDecodeError, OSError):
continue
for number, line in enumerate(lines, 1):
for model, pattern in PATTERNS.items():
if pattern.search(line):
yield path.relative_to(root), number, model, line.strip()
def main() -> int:
root = Path(sys.argv[1] if len(sys.argv) > 1 else ".").resolve()
hits = list(scan(root))
for path, number, model, text in sorted(hits):
print(f"{path}:{number}: {model} -> {RETIRING[model]}\n {text}")
print(f"{len(hits)} pinned reference(s)")
return 1 if hits else 0
if __name__ == "__main__":
sys.exit(main())
Two details matter. The lookarounds in the pattern stop gpt-5.5 matching inside a longer string such as gpt-5.55, which a plain word boundary would get wrong because . and - aren’t word characters. And the exit code is 1 when anything is found, so the same script works as a CI check after the migration, to stop the old ID creeping back in.
Run it
Take a directory with a config file, a workflow, a shell script and a source file that mentions a similar-looking ID:
config.toml model = "gpt-5.5"
.github/workflows/nightly.yml codex exec -m gpt-5.5 "Summarise open failures"
scripts/notes.sh codex exec --model gpt-5.3-codex-spark "Draft release notes"
src/app.ts // gpt-5.55 is not a real ID; const m = "gpt-6.1-sol";
Run python3 find_pins.py . and it prints:
.github/workflows/nightly.yml:5: gpt-5.5 -> gpt-6-sol (or gpt-6.1-sol)
- run: codex exec -m gpt-5.5 "Summarise open failures"
config.toml:1: gpt-5.5 -> gpt-6-sol (or gpt-6.1-sol)
model = "gpt-5.5"
scripts/notes.sh:2: gpt-5.3-codex-spark -> a model under 'recommended models'
codex exec --model gpt-5.3-codex-spark "Draft release notes"
3 pinned reference(s)
The exit code is 1. The src/app.ts lines are correctly ignored: one mentions gpt-5.55, the other already uses gpt-6.1-sol. After swapping the three IDs for gpt-6-sol and gpt-6-luna, a second run prints 0 pinned reference(s) and exits 0.
Pass a different root to scan more than one project. Point it at a directory that holds all your checkouts and it finds the pins in repositories you forgot you owned.
What a file scan can’t see
The changelog’s list of places to update is longer than your working tree. It names workspace defaults, saved model settings, managed configurations, custom agents, scheduled tasks and scripts. Only some of those are files in a repository.
Workspace defaults and managed configurations are set by whoever administers your Codex workspace, so a team member with a repo and no admin rights can’t fix them. Ask. Saved model settings live in whatever client you last picked a model in, which is why /model in each client is worth a glance. Scheduled tasks that you created in the product, not in a crontab or workflow file, won’t appear in a grep at all.
Custom agents deserve a specific look. An agent definition that names a model is a pin like any other, and it usually sits in a file that nobody opens until it breaks. The script finds it if it’s under the directory you scan. It can’t find one stored outside it.
Keep the pin in one place
Once the old IDs are gone, the better move is to make sure the next retirement is a one-line change. In shell scripts, read the model from a single variable and give it a default:
CODEX_MODEL="${CODEX_MODEL:-gpt-6.1-sol}"
codex exec -m "$CODEX_MODEL" "Draft release notes from the last ten commits"
Every script now shares one default, and a single job can override it from its environment when it needs Luna instead. That’s plain shell, not a Codex feature, which is the point: it works the same whichever way a script is launched.
For the repository itself, add the audit as a workflow step so a retired ID can’t be reintroduced in a pull request. The list of retiring IDs in the script is the only thing to maintain. Add a new entry whenever a changelog announces a retirement.
name: model-pins
on: [pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python3 tools/find_pins.py .
The step fails with exit code 1 when it finds a pin and prints each file and line, so the reviewer sees what to change. One caveat: the script scans every text file under the root, so a changelog or migration note that quotes gpt-5.5 on purpose will trip it. Either reword the note or add its folder to SKIP_DIRS.
Where this goes wrong
Replacing the ID isn’t the same as keeping the behaviour. A different model reasons differently, formats differently and costs differently. A script that parses the agent’s output, such as a verdict line or a JSON field, deserves a rerun on a few real inputs before the swap goes live. Where output is structured with a schema, as in the codex exec CI gate , the schema catches format drift. Free-text parsing doesn’t get that protection.
The other trap is a find-and-replace that’s too eager. gpt-6-sol and gpt-6.1-sol are different models, and the docs recommend the second for complex coding work when your account has it. Check what each plan and client can select before you hard-code either. If a job doesn’t need to pick, the cleaner fix is to delete the pin and let Codex use its recommended model, at the price of the job’s behaviour changing whenever that recommendation does.
Don’t bother with any of this if every Codex job you run uses an API key and no ChatGPT sign-in. The retirement notice doesn’t cover the API.
The first 15 minutes
Copy the script into a scratch location, point it at the directory that holds your repositories, and read the list. Fix the pins in files you own first, then send the workspace-level ones to your admin with the 14 October date attached. Keep the script around: run it with the exit code in a CI step once the migration is done.