This is evidence for other users. I’m not asking for an explanation.
What the server does
Codex sends X-Codex-Turn-State on every request. Two lengths appear:
292 characters -> you get the model you asked for
312 characters -> the response body's own model field says gpt-5.6-luna
Measured on a Pro 20x account across nine hours:
312 responses observed: 343
of those, response body carries a model field: 247
of those 247, model == gpt-5.6-luna: 247 (100%)
Every one of those requests asked for gpt-6-astra. The routing hint said
gpt-6-astra. The client showed gpt-6-astra. The server ran gpt-5.6-luna.
This is not the model describing itself. It is the server’s own model
field in response.created, response.in_progress and
response.completed.
Nothing in the client tells you this happened.
The healthy 292 is issued on the server’s schedule, not yours
In those nine hours the 292 was issued exactly three times:
22:38:18
22:40:34
03:27:41 <- after a restart and hours of no requests
Between 20:46 and 03:27 - seven hours and forty-one minutes - the server
issued no healthy Turn-State at all. Every request in that window came
back on the 312 path.
A long idle period followed by a fresh client gets you one healthy window.
Mine lasted 55 minutes, then stopped.
The same connection produces both outcomes
Same account, same client, same prompt, same requested model, same London
VPS egress, eleven minutes apart:
312-character Turn-State -> gpt-5.6-luna
292-character Turn-State -> gpt-6-astra
Same egress, same Cloudflare POP, same gateway. So the decision is not
being made from the request. It is being made from something recorded
against the account or the connection.
Other users running probes across different connections report the same
shape: the odds of being issued a 292 shift with residential versus
datacenter ranges, region, ASN, and IPv6 pools - and that switching
account or connection changes results materially.
Check your own account
RUST_LOG='codex_api::sse::responses=trace' codex exec \
--skip-git-repo-check -s read-only -m 'gpt-6-astra' 'hi' \
2>&1 >/dev/null | rg -o --replace '$1' '"model":"([^"]+)"' | head -n1
Prints gpt-5.6-luna: your request is being served by a different model
than the one you selected. Prints gpt-6-astra: it isn’t.
The Turn-State length is in the response headers. Capture it at the same
time - that’s the other half of the picture.
Why this matters
If your account or your connection has been marked such that it cannot be
issued a healthy Turn-State, then no client-side change will fix it. You
can change your prompt, your settings, your session, your exit node. The
server decides.
And you will never be told. The model selector keeps showing what you
picked.
I have the raw logs for every request: timestamps, Turn-State lengths,
response body model fields, cf-ray values. Reproduce it on your own
account before you take my word for any of it.
I know this probably won’t change anything, but I’m posting it so other
people have strong evidence when they appeal to OpenAI.
