Client or integration
Codex App
Area
Authentication and account pool
Summary
Important control result: this K12 account has working Codex access. Prompts succeed in the official Codex product, and the same account previously also worked through OpenCodex. The current OpenCodex behavior is a regression.
This is therefore not a report that Codex is unavailable for K12, nor evidence that the workspace administrator disabled Codex. The exact first failing OpenCodex version is not known.
The account can be added and reauthenticated successfully in OpenCodex. OpenCodex stores a fresh credential, warm-up validation succeeds, and authenticated WHAM usage returns HTTP 200 with plan_type: "k12". Despite that, a prompt routed through OpenCodex immediately receives HTTP 403 with codex_workspace_access_denied.
OpenCodex then classifies that workspace-specific 403 as a credential failure and marks the account needs-reauth. Reauthentication succeeds, but the next OpenCodex-routed prompt recreates the same state.
Expected: OpenCodex should send the workspace context required for a working K12 Codex account. If upstream still returns a workspace-specific denial, OpenCodex should surface it as a workspace/request-context problem and keep the valid credential instead of presenting reauthentication as the remedy.
Reproduction
- Historical control: the same K12 account previously completed prompts successfully through OpenCodex.
- Current control: sign in with the K12 account directly in the official Codex product and send a prompt. It still succeeds.
- The official Codex Analytics page for the same account also shows active 5-hour and weekly usage limits.
- Add that same account through OpenCodex Codex Auth and complete OAuth.
- Confirm OpenCodex detects
plan_type: "k12"; credential warm-up and WHAM usage both succeed.
- Select the K12 pool account and send a minimal prompt through OpenCodex using
gpt-5.6-sol or gpt-5.6-luna.
- The OpenCodex-routed request returns HTTP 403 with
codex_workspace_access_denied, and OpenCodex marks the account needs-reauth.
- Complete Re-authenticate and retry immediately. The credential validates successfully, but the same OpenCodex-only failure returns.
This reproduced for both tested models on every attempt. The successful direct Codex prompt distinguishes this from missing K12 entitlement; the earlier successful OpenCodex use additionally identifies the current behavior as a regression.
Version
2.20.0
Operating system
macOS 27.0 (arm64)
Provider and model
OpenAI / ChatGPT Codex workspace login (plan_type=k12); gpt-5.6-sol and gpt-5.6-luna
Logs or error output
Sanitized upstream response observed only on the OpenCodex-routed requests:
{
"detail": {
"message": "Unauthorized: Contact your ChatGPT workspace administrator for access.",
"code": "codex_workspace_access_denied"
}
}
Sanitized sequence:
direct prompt in official Codex product: succeeds
OpenCodex credential warm-up / validation: succeeds
OpenCodex WHAM usage: HTTP 200, authenticated, plan_type=k12
prompt via OpenCodex with gpt-5.6-sol: HTTP 403
prompt via OpenCodex with gpt-5.6-luna: HTTP 403
OpenCodex account status afterward: needs-reauth
Implementation clue in v2.20.0: classifyCodexUpstreamOutcome() in src/codex/routing.ts classifies every 401/403 as credential; that branch calls markAccountNeedsReauth(). The upstream code here explicitly describes workspace access, while the same credential is freshly validated and accepted by WHAM.
Screenshots and supporting files
Related K12 quota-window parser issue: #1791
Redacted configuration
{
"codexAccounts": [
{
"plan": "k12",
"isMain": false
}
]
}
Emails, account/workspace IDs, tokens, request IDs, and local paths were intentionally omitted.
Checks
Client or integration
Codex App
Area
Authentication and account pool
Summary
This is therefore not a report that Codex is unavailable for K12, nor evidence that the workspace administrator disabled Codex. The exact first failing OpenCodex version is not known.
The account can be added and reauthenticated successfully in OpenCodex. OpenCodex stores a fresh credential, warm-up validation succeeds, and authenticated WHAM usage returns HTTP 200 with
plan_type: "k12". Despite that, a prompt routed through OpenCodex immediately receives HTTP 403 withcodex_workspace_access_denied.OpenCodex then classifies that workspace-specific 403 as a credential failure and marks the account
needs-reauth. Reauthentication succeeds, but the next OpenCodex-routed prompt recreates the same state.Expected: OpenCodex should send the workspace context required for a working K12 Codex account. If upstream still returns a workspace-specific denial, OpenCodex should surface it as a workspace/request-context problem and keep the valid credential instead of presenting reauthentication as the remedy.
Reproduction
plan_type: "k12"; credential warm-up and WHAM usage both succeed.gpt-5.6-solorgpt-5.6-luna.codex_workspace_access_denied, and OpenCodex marks the accountneeds-reauth.This reproduced for both tested models on every attempt. The successful direct Codex prompt distinguishes this from missing K12 entitlement; the earlier successful OpenCodex use additionally identifies the current behavior as a regression.
Version
2.20.0
Operating system
macOS 27.0 (arm64)
Provider and model
OpenAI / ChatGPT Codex workspace login (
plan_type=k12);gpt-5.6-solandgpt-5.6-lunaLogs or error output
Sanitized upstream response observed only on the OpenCodex-routed requests:
{ "detail": { "message": "Unauthorized: Contact your ChatGPT workspace administrator for access.", "code": "codex_workspace_access_denied" } }Sanitized sequence:
Implementation clue in v2.20.0:
classifyCodexUpstreamOutcome()insrc/codex/routing.tsclassifies every 401/403 ascredential; that branch callsmarkAccountNeedsReauth(). The upstream code here explicitly describes workspace access, while the same credential is freshly validated and accepted by WHAM.Screenshots and supporting files
Related K12 quota-window parser issue: #1791
Redacted configuration
{ "codexAccounts": [ { "plan": "k12", "isMain": false } ] }Emails, account/workspace IDs, tokens, request IDs, and local paths were intentionally omitted.
Checks