Skip to content

V2: ChatGPT subscription (OpenAI OAuth) not routed to codex backend — HTTP 401 missing api.responses.write #34765

Description

@kitlangton

Summary

On the v2 branch, using a ChatGPT Pro/Plus subscription (OpenAI OAuth login) to run an OpenAI model (e.g. GPT-5.5) fails immediately with:

Provider request failed with HTTP 401: {
  "error": {
    "message": "You have insufficient permissions for this operation. Missing scopes: api.responses.write. Check that you have the correct role in your organization (Reader, Writer, Owner) and project (Member, Owner), and if you're using a restricted API key, that it has the necessary scopes.",
    "type": "invalid_request_error",
    "param": null,
    "code": null
  }
}

The request fails in ~1-2ms (rejected before generation). This worked in V1.

Root cause

V2 sends the ChatGPT-subscription OAuth access token to the standard https://api.openai.com/v1/responses endpoint as a plain bearer token, without:

  1. the chatgpt-account-id header, and
  2. the ChatGPT/codex backend base URL (https://chatgpt.com/backend-api/codex/responses).

A ChatGPT-plan token is not an API-key-equivalent credential for the public API, so OpenAI rejects it with Missing scopes: api.responses.write. It's not an account/key/scope problem on the user side — the ChatGPT-subscription request path is incomplete in V2.

Evidence

  • The V2 OpenAI plugin (packages/core/src/plugin/provider/openai.ts) implements the ChatGPT OAuth login flow and even extracts the account id into credential.metadata.accountID (extractAccountID/claim), but nothing consumes it — no code sets chatgpt-account-id or rewrites the endpoint.
  • The model resolver packages/core/src/session/runner/model.ts (fromCatalogModel) turns an oauth credential into a bare Auth.bearer(access) against the default OpenAIResponses.route, and only applies credential.metadata for type === "key", never for oauth.
  • Repo-wide, the only references to chatgpt-account-id / backend-api / codex/responses live in V1 (packages/opencode/src/plugin/openai/codex.ts) — the V2 packages have none.

How V1 does it (reference)

packages/opencode/src/plugin/openai/codex.ts (CodexAuthPlugin) uses an auth loader returning a custom fetch that, for OAuth credentials:

  • strips the SDK's Authorization and uses a dummy api key,
  • refreshes the token and re-extracts the account id,
  • sets authorization: Bearer <access> and ChatGPT-Account-Id: <accountId>,
  • rewrites /v1/responses (and /chat/completions) to https://chatgpt.com/backend-api/codex/responses,
  • filters the catalog to ChatGPT-plan-eligible models (ALLOWED_MODELS, plus >5.4).

Proposed fix (V2)

V2 already runs OpenAI through the native @opencode-ai/llm package, whose Route/Auth/Endpoint algebra makes this declarative (no fetch monkey-patch, no dummy-key dance). In fromCatalogModel (model.ts), when the credential is a ChatGPT OAuth method (methodID chatgpt-browser/chatgpt-headless with metadata.accountID):

withDefaults(resolved, OpenAIResponses.route)
  .with({
    endpoint: { baseURL: "https://chatgpt.com/backend-api/codex" }, // + "/responses"
    auth: Auth.bearer(key).andThen(Auth.headers({ "chatgpt-account-id": accountID })),
  })
  .model({ id: resolved.api.id })

Plus a catalog refinement in packages/core/src/plugin/provider/openai.ts (extend the existing ctx.catalog.transform) to enable only ChatGPT-plan-eligible models under an OAuth credential.

Confirmed facts:

  • OpenAIResponses.PATH = "/responses", DEFAULT_BASE_URL = "https://api.openai.com/v1"; Endpoint.render joins baseURL + path.
  • Credential.OAuth carries methodID and metadata (packages/schema/src/credential.ts), both available in fromCatalogModel.
  • The AISDK hooks (packages/core/src/aisdk.ts) do not receive the credential, so the LLM-path fix is preferred over the AI SDK fetch-wrapper approach.

Environment

  • Branch: v2 @ 6a91a682e4
  • Repro: authenticate OpenAI via ChatGPT Pro/Plus OAuth, select a GPT-5.x model, send any prompt.

Metadata

Metadata

Assignees

Labels

2.0bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions