Usage-limit rejections are undiagnosable: no request ID, /status contradicts the server, and resetsAt slides for uninitialized windows
What version of Codex CLI is running?
0.147.0
What subscription do you have?
Plus
Which model were you using?
gpt-5.6-terra
What platform is your computer?
Darwin 25.5.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
WezTerm 20240203-110809-5046fc22 (no multiplexer)
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1786724724s since unix epoch",
"overallStatus": "ok",
"codexVersion": "0.147.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "/Users/USER/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/Users/USER/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "/Users/USER/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "/Users/USER/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "/Users/USER/.codex/app-server-daemon/app-server-updater.pid (missing)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "/Users/USER/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/Users/USER/.codex",
"config.toml": "/Users/USER/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "/Volumes/Crucial X9",
"enabled feature flags": "shell_tool, view_image, unified_exec, shell_snapshot, code_mode_host, terminal_resize_reflow, sqlite, hooks, enable_request_compression, multi_agent, apps, tool_search_always_defer_mcp_tools, tool_suggest, plugins, in_app_browser, in_app_updates, browser_use, browser_use_full_cdp_access, browser_use_external, computer_use, remote_plugin, plugin_sharing, image_generation, resize_all_images, item_ids, skill_mcp_dependency_install, skill_search, mentions_v2, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, auth_elicitation, personality, fast_mode, tui_app_server, remote_compaction_v2, workspace_dependencies",
"feature flag overrides": "none",
"feature flags enabled": "39",
"log dir": "/Users/USER/.codex/log",
"mcp servers": "2",
"model": "gpt-5.6-terra",
"model provider": "openai",
"sqlite home": "/Users/USER/.codex"
},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.48.1",
"details": {
"PATH git #1": "/opt/homebrew/bin/git",
"PATH git #2": "/usr/bin/git",
"PATH git entries": "2",
"git build options": "git version 2.48.1; cpu: arm64; no commit associated with this build; sizeof-long: 8; sizeof-size_t: 8; shell-path: /bin/sh; feature: fsmonitor--daemon; libcurl: 8.7.1; zlib: 1.2.12",
"git exec path": "/opt/homebrew/opt/git/libexec/git-core",
"git version": "git version 2.48.1",
"repo detected": "false",
"selected git": "/opt/homebrew/bin/git"
},
"remediation": null,
"durationMs": 130
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/Users/USER/.nvm/versions/node/v24.15.0/bin/codex",
"current executable": "/Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex",
"install context": "npm (package /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin, bin /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin, resources /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex-resources, path /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex-path)",
"managed by bun": "false",
"managed by npm": "true",
"managed by pnpm": "false",
"managed package root": "/Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex",
"npm update target": "/Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex"
},
"remediation": null,
"durationMs": 181
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "MCP configuration is locally consistent",
"details": {
"configured servers": "2",
"disabled servers": "1",
"stdio servers": "2"
},
"remediation": null,
"durationMs": 0
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"proxy env vars": "none"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 403)",
"reachability mode": "ChatGPT auth"
},
"remediation": null,
"durationMs": 201
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "2 IPv4, 0 IPv6, first IPv4",
"auth mode": "chatgpt",
"connect timeout": "15000 ms",
"endpoint": "wss://chatgpt.com/backend-api/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"models etag present": "true",
"provider name": "OpenAI",
"proxy env vars": "none",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 994
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running npm on macos-aarch64",
"details": {
"commit": "unknown",
"current executable": "/Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex",
"install method": "npm (package /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin, bin /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin, resources /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex-resources, path /Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex-path)",
"platform": "macos-aarch64",
"version": "0.147.0"
},
"remediation": null,
"durationMs": 1
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "/Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/codex-path/rg",
"search command readiness": "file exists",
"search provider": "bundled"
},
"remediation": null,
"durationMs": 0
},
"sandbox.helpers": {
"id": "sandbox.helpers",
"category": "sandbox",
"status": "ok",
"summary": "sandbox configuration is readable",
"details": {
"approval policy": "OnRequest",
"codex-linux-sandbox helper": "none",
"execve wrapper helper": "/Users/USER/.codex/tmp/arg0/codex-arg0Xt4kc3/codex-execve-wrapper",
"filesystem sandbox": "restricted",
"network sandbox": "restricted"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/Users/USER/.codex (dir)",
"active rollout files": "91 files, 72962197 total bytes, 801782 average bytes",
"archived rollout files": "1 files, 101653 total bytes, 101653 average bytes",
"goals DB": "/Users/USER/.codex/goals_1.sqlite (file)",
"goals DB integrity": "ok",
"log DB": "/Users/USER/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "/Users/USER/.codex/log (dir)",
"memories DB": "/Users/USER/.codex/memories_1.sqlite (file)",
"memories DB integrity": "ok",
"queue DB": "/Users/USER/.codex/queue_1.sqlite (file)",
"queue DB integrity": "ok",
"sqlite home": "/Users/USER/.codex (dir)",
"state DB": "/Users/USER/.codex/state_5.sqlite (file)",
"state DB integrity": "ok",
"thread history DB": "/Users/USER/.codex/thread_history_1.sqlite (file)",
"thread history DB integrity": "ok"
},
"remediation": null,
"durationMs": 232
},
"state.rollout_db_parity": {
"id": "state.rollout_db_parity",
"category": "threads",
"status": "ok",
"summary": "rollout files and state DB thread inventory agree",
"details": {
"default model provider": "openai",
"rollout DB active files": "91",
"rollout DB active rows": "91",
"rollout DB archive mismatches": "0",
"rollout DB archived files": "1",
"rollout DB archived rows": "1",
"rollout DB duplicate DB paths": "0",
"rollout DB duplicate rollout thread ids": "0",
"rollout DB malformed file names": "0",
"rollout DB missing active rows": "0",
"rollout DB missing archived rows": "0",
"rollout DB model providers": "openai=92",
"rollout DB rows": "92",
"rollout DB scan cap reached": "false",
"rollout DB scan errors": "0",
"rollout DB sources": "cli=72, subagent:thread_spawn=9, exec=4, vscode=3, subagent:other=2, subagent:review=2",
"rollout DB stale rows": "0"
},
"remediation": null,
"durationMs": 216
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language en-IN",
"details": {
"EDITOR": "not set",
"LANG": "en_IN.UTF-8",
"VISUAL": "not set",
"os": "Mac OS 26.5.2 [64-bit]",
"os language": "en-IN",
"os type": "Mac OS",
"os version": "26.5.2"
},
"remediation": null,
"durationMs": 11
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"COLORTERM": "truecolor",
"TERM_PROGRAM": "WezTerm",
"color output": "disabled (stdout is not a terminal)",
"effective locale": "en_IN.UTF-8",
"stderr is terminal": "false",
"stdin is terminal": "false",
"stdout is terminal": "false",
"terminal": "WezTerm",
"terminal size": "80x24",
"terminal version": "20240203-110809-5046fc22"
},
"remediation": null,
"durationMs": 16
},
"terminal.title": {
"id": "terminal.title",
"category": "title",
"status": "ok",
"summary": "terminal title default",
"details": {
"terminal title activity": "true",
"terminal title items": "activity, project-name",
"terminal title project source": "cwd",
"terminal title project value": "Crucial X9",
"terminal title source": "default"
},
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "ok",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.147.0",
"check for update on startup": "true",
"last checked at": "2026-08-14T13:32:42.670423Z",
"latest version": "0.147.0",
"latest version status": "current version is not older",
"npm update target": "/Volumes/Crucial X9/.nvm/versions/node/v24.15.0/lib/node_modules/@openai/codex",
"update action": "npm install -g @openai/codex",
"version cache": "/Users/USER/.codex/version.json"
},
"remediation": null,
"durationMs": 754
}
}
}
What issue are you seeing?
When the server rejects a turn with a usage-limit error, the CLI gives the user no way to diagnose or report it. Three separate behaviors combine to make this class of failure effectively undebuggable, and I hit all three while investigating a persistent rejection.
Scope note: the underlying account-state problem is being handled separately by support, and I'm not asking anyone here to fix my account. This issue is only about the three client-observable defects below, each of which would affect any user hitting a usage-limit rejection.
1. Usage-limit rejections carry no request ID
The websocket upgrade to wss://chatgpt.com/backend-api/codex/responses succeeds — HTTP 101, auth header attached, and the client logs event.name="codex.websocket_connect" success="true". The rejection then arrives as an application-level SSE frame:
event.name="codex.sse_event" event.kind=response.completed error.message=You've hit your usage limit. ...
Because the failure is a frame on an already-upgraded socket rather than an HTTP response, there is no status code and no x-oai-request-id anywhere in the client's output or logs. The feedback_tags line for the failing request logs auth_request_id="" and auth_cf_ray="", both empty.
The practical consequence: a blocked user asked by support for "the request ID of a failed call" has nothing to provide, and support searching for a 429 on the account finds nothing, because no 429 was ever issued. I only obtained usable identifiers by re-running under RUST_LOG=debug and scraping the websocket handshake response headers for a cf-ray.
Expected: usage-limit rejections should surface a correlatable identifier in the user-facing error, or at minimum record the connection's cf-ray and any server-supplied request ID against the failing turn.
2. /status contradicts the server and gives no indication of why a turn was refused
account/rateLimits/read returns, on an account where every single turn is being refused:
{"limitId":"codex","planType":"plus","primary":{"usedPercent":0,"windowDurationMins":10080},"credits":{"hasCredits":false,"unlimited":false,"balance":"0"},"spendControlReached":false,"rateLimitReachedType":null}
usedPercent: 0 and rateLimitReachedType: null — the client is told the account is explicitly not rate limited — while /responses refuses every turn on that same account, seconds apart, with the same auth header.
For contrast, in the same session 1.13 seconds before a refused turn, GET /backend-api/codex/models returned 200 OK with a normal x-oai-request-id. So the client can reach the API fine; only the metering gate on /responses refuses, and nothing in /status reflects that.
Expected: when /responses refuses a turn on usage-limit grounds, /status should not report the account as unlimited and unblocked. If the two are served by systems that can disagree, the client should surface that rather than presenting one of them as the truth.
3. resetsAt for an uninitialized window slides forward on every read
Sampling account/rateLimits/read repeatedly over about 30 minutes, resetsAt was always exactly now + windowDuration:
15:36:27Z -> resetsAt = 2026-08-21T15:36:27Z
15:41:23Z -> resetsAt = 2026-08-21T15:41:16Z
16:01:40Z -> resetsAt = 2026-08-21T16:01:33Z
A window that has genuinely been opened has a fixed anchor and counts down. A window that has never been initialized apparently reports now + 7 days forever, and is indistinguishable from a healthy, unused one. Meanwhile the refusal from /responses quotes a fixed reset timestamp that never moves — I have 40 logged instances over several hours, all identical to the second.
That divergence, a sliding anchor in one place and a fixed one in the other, was the clearest signal that two different quota records were in play. It took hours to spot precisely because the sliding value looks perfectly healthy in isolation.
Expected: either don't return a resetsAt for a window with no recorded usage, or flag it distinguishably, so a never-initialized window can't be mistaken for an unused one.
4. codex doctor reports everything healthy
Included in the doctor report field above: all 18 checks return ok and overallStatus is ok, including auth.credentials, network.provider_reachability, and network.websocket_reachability — on a machine where every single turn is refused. This is the same problem again: nothing the CLI can show the user reflects the failure.
Why this matters
Individually these are small. Together they mean a blocked user cannot tell why, cannot obtain an identifier to report it with, and is shown status output and diagnostics that positively contradict what the server is doing. Support then asks for a request ID that does not exist and searches for a 429 that was never sent.
Improving any one of them — most cheaply the first — would materially shorten diagnosis for this whole class of failure.
Full error text
ERROR: You've hit your usage limit. Upgrade to Plus to continue using Codex (https://chatgpt.com/explore/plus), or try again at Sep 13th, 2026 7:11 PM.
Reproduces identically on Codex Desktop 26.810.41047 as well as the CLI.
What steps can reproduce the bug?
Requires an account whose /responses metering gate refuses turns. In my case the account holds an active Plus subscription that the metering path does not honour, so every turn is refused while all client-side status surfaces report the account as healthy.
1. Trigger a refused turn:
codex exec --sandbox read-only --skip-git-repo-check "Reply with exactly: OK"
Result: ERROR: You've hit your usage limit. Upgrade to Plus to continue using Codex (...), or try again at Sep 13th, 2026 7:11 PM.
No request ID, no status code, and no correlatable identifier is printed.
2. Confirm the failure carries no identifier, and that the websocket actually connected:
RUST_LOG=debug codex exec --sandbox read-only --skip-git-repo-check "Reply with exactly: OK" 2>&1 | grep -E 'websocket_connect|sse_event|auth_request_id'
Observe codex.websocket_connect ... success="true" immediately followed by the codex.sse_event ... event.kind=response.completed error.message=You've hit your usage limit frame, and auth_request_id="" / auth_cf_ray="" on the failing request. A cf-ray exists only in the handshake response headers, and only at debug verbosity.
3. Compare against /status in the same session — it reports planType: plus, usedPercent: 0, rateLimitReachedType: null, i.e. explicitly not rate limited, contradicting step 1.
4. Observe the sliding anchor by reading account/rateLimits/read several times over ~30 minutes. resetsAt advances to exactly now + 7 days on every read, while the reset timestamp quoted in the step 1 error never changes.
5. Run codex doctor --json — all 18 checks return ok.
Thread IDs from two refused turns:
01a000e9-fdc7-7353-bae5-36908c93d867 (2026-08-14T15:35:34.095Z)
01a000f3-c1f5-7123-9a53-5d799178ce07 (2026-08-14T15:46:11.564Z)
For the second of those, GET /backend-api/codex/models returned 200 OK at 15:46:10.434Z — 1.13 seconds before the refusal at 15:46:11.564Z, on the same connection auth. Full debug logs, websocket cf-ray values, and paired request timings are available on request; I'd rather send those privately than attach them here, since they're account-linked.
What is the expected behavior?
A user whose turns are being refused should be able to find out why, and should have something concrete to hand to support. Specifically:
1. The rejection should carry a correlatable identifier. Even though the failure arrives as an SSE frame on an already-upgraded websocket rather than as an HTTP response, the client already has the handshake response headers, including cf-ray. That should be attached to the failing turn and shown in the error, instead of auth_request_id="" and auth_cf_ray="". Right now the user is asked by support for a request ID that does not exist.
2. /status should not contradict the server. If /responses is refusing turns on usage-limit grounds, /status should not simultaneously report usedPercent: 0 and rateLimitReachedType: null. If the two are served by systems that can legitimately disagree, the client should say the entitlement and metering state conflict rather than presenting one as the truth.
3. An uninitialized window should be distinguishable from an unused one. A resetsAt that recomputes to now + windowDuration on every read should either not be returned at all, or be flagged, so it can't be mistaken for a healthy window that simply has not been consumed yet.
4. codex doctor should be able to detect this. A machine where every turn is refused should not report overallStatus: ok across all 18 checks. A check that issues a minimal turn, or that compares /status against the last observed refusal, would catch it.
Concretely, in my case the correct behavior would have been an error along the lines of:
ERROR: Your account has an active Plus subscription but the usage-limit service is refusing requests (ref: cf-ray a2b117407d2e1849-DEL, thread 01a000f3-c1f5-7123-9a53-5d799178ce07). This is inconsistent with /status, which reports 0% usage. Please contact support with this reference.
Instead of a bare instruction to upgrade to a plan the user already has.
Additional information
The error text itself is misleading in this state. It reads "Upgrade to Plus to continue using Codex" and links to the upgrade page, on an account that already holds Plus. A user who trusts that message would buy a second subscription and still be blocked. Whatever the underlying entitlement state, the client should not instruct a Plus subscriber to purchase Plus.
This is not a transient or local-state problem. Before filing, I ruled out:
codex logout+codex login(full OAuth re-authentication, not a token refresh), repeated many times over several hours- "Log out of all devices" server-side, invalidating all sessions
- Both clients: CLI (
execand TUI) and Desktop 26.810.41047, identical failure - No
OPENAI_API_KEYorCODEX_API_KEYin the environment — logs confirmauth.env_openai_api_key_present=false - Freshly minted tokens: the access token carries
chatgpt_plan_type: "plus"with the subscription re-verified server-side at login, and a turn ~60 seconds later is still refused
On why I filed this as a CLI issue rather than only contacting support. The account-state problem is with support and is theirs to fix. But the four behaviors above are client-side and would affect anyone hitting a usage-limit refusal, whatever its cause. I spent several hours reconstructing information that the CLI already had and could have surfaced — the websocket cf-ray was sitting in the handshake response headers the whole time, visible only at RUST_LOG=debug. Surfacing it would have turned hours of log archaeology into a single copy-paste.
I'm happy to test a patch against this account, since it currently reproduces the failure on every single request.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Resolved — root cause was stale local daemon state, not server-side entitlement
Posting the fix in full, since several other reports (#38354, #37250, and the closed #38379) describe the same symptoms and at least one person there has said re-authentication did not help them.
What fixed it
Plain
codex logout && codex logindoes not clear everything. It removes~/.codex/auth.jsonbut leaves orphaned helper processes and the IPC socket behind, and those pin the pre-upgrade entitlement. I had run logout/login many times over several hours with no effect, and had also done a server-side "log out of all devices" on the ChatGPT website, also with no effect.What worked was clearing that state as well, before logging back in:
First turn after that succeeded immediately. Two consecutive turns confirmed, tokens consuming normally.
In my case the orphaned processes were
Codex Framework.framework/.../browser_crashpad_handler, still alive nearly three hours after the desktop app itself had exited. This is the same "fully quit" step that resolved #38379 — but on the CLI it isn't obvious that anything is still running, because the main app is gone and only helpers remain.If you are hitting this after a Free → Plus transition, try the above before assuming it is a server-side problem. Mine looked exactly like a backend entitlement bug and was not.
Correction to this issue: defect 3 is withdrawn
I claimed that a
resetsAtwhich slides tonow + windowDurationon every read indicates an uninitialized window. That was wrong. With everything working and ~20k tokens consumed,resetsAtstill slides andusedPercentstill reports 0. That is simply how the rolling window reports, and it is not evidence of anything. Apologies for the noise — striking that point.Defects 1, 2 and 4 still stand
They are why this took hours instead of minutes:
x-oai-request-id. Support asked me for the request ID of a failed call and I had nothing to give them. Thecf-rayexisted in the handshake response headers the whole time, visible only underRUST_LOG=debug./statuscontradicted the server. It reportedplanType: plus,usedPercent: 0,rateLimitReachedType: null— explicitly not rate limited — while every turn was refused seconds later on the same auth.codex doctorreturned 18/18ok,overallStatus: okon a completely non-functional install, includingauth.credentialsandnetwork.websocket_reachability.All three pointed away from local state, which is the one place the problem actually was. A
doctorcheck that compares/statusagainst the last observed refusal, or that flags orphaned helper processes and a stale IPC socket, would have caught this immediately. So would surfacing thecf-rayin the user-facing error.Happy for maintainers to close this or narrow it to the diagnostics points, whichever is more useful.