Skip to content

[OpenAI/Codex] K12 denial under detail.code is misclassified and cross-account thread switching returns 403 #2046

Description

@jojodat

Client or integration

Codex App

Provider or upstream service

OpenAI/Codex (K12)

OpenCodex version

2.24.1

Endpoint or capability

Authentication and account/thread routing

Current behaviour

The published denial classifier recognizes structured codes only at the top level or under error.code. The observed response shape is:

{"detail":{"code":"codex_workspace_access_denied","message":"..."}}

A local regression test returned denial = null before the patch. Adding an own-property check for detail.code returned denial = "workspace", and the incorrect needs-reauth state disappeared.

A fresh task bound exactly to the K12 selector completed with HTTP 200 and was routed to the K12 pool account. Switching an existing Plus-bound task to the same exact K12 selector produced four reproducible HTTP 403 responses without fallback.

Expected behaviour

K12 should continue to work in clean tasks, and workspace denials nested under own-property detail.code must be classified as workspace denials rather than credential failures.

When an existing Plus-bound thread is switched to an exact K12 selector, OpenCodex must either start clean/remove incompatible thread metadata or return a clear thread/account incompatibility. It must never mark the account needs-reauth for this case.

Minimal redacted request or reproduction

1. Reauthenticate a valid K12 account and verify the upstream plan is K12.
2. Start a fresh task explicitly bound to that K12 account and select `k12test/gpt-5.6-sol`; observe HTTP 200.
3. Start a task under Plus, then switch that existing task to the same exact K12 selector.
4. Observe reproducible HTTP 403 responses with no fallback.
5. Unit-test denial classification with `{ detail: { code: "codex_workspace_access_denied" } }`; current result is `null` instead of `workspace`.

Actual response or error

Fresh exact-K12 task: HTTP 200.

Existing Plus-bound task switched to exact K12: four reproducible HTTP 403 responses, no fallback. The nested denial envelope is:


{"detail":{"code":"codex_workspace_access_denied","message":"..."}}


OpenCodex incorrectly transitions to `needs-reauth` unless `detail.code` is handled.

Upstream documentation

No public upstream specification appears to document this private WHAM denial envelope. The concrete client requirement is established by the observed response shape, successful clean-task routing, and the reproducible cross-account/thread case.

Suggested mapping or implementation notes

Extend structured denial extraction to accept own-property detail.code, alongside the existing top-level and error.code paths. Add a regression test: an existing Plus-bound thread switched to an exact K12 selector must either shed incompatible metadata/start clean or return an explicit thread/account incompatibility, never needs-reauth.

Additional context and attachments

Follow-up to #1789. Reauthentication succeeded, credentials were valid, and the authenticated upstream check returned HTTP 200 with the K12 plan. This confirms that K12 works through OpenCodex in a clean task; the remaining failure is specific to cross-account/thread metadata.

No additional private data should be necessary. If maintainers need a diagnostic build tested, I can offer one anonymized K12 run.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Metadata

Metadata

Assignees

Labels

account-poolOAuth, credentials, Codex pool, quota, failover, plansproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reports

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions