Skip to content

Fleet workhorse legs failing fleet-wide: provider.auth 401 'Missing scopes: api.responses.write' (15:40 wave 9/9 roots failed) #3216

Closed

Description

@dezren39

Found by the OpenChamber conductor, cycle 2026-10-31 15:32 CDT (content-verified from session messages, scratch conductor-scratch-2026-10-31-1532/msgs-new.txt).

Symptom

The entire 15:40 CDT lane wave failed: 9/9 root sessions on opencode-route/workhorse (code x6, openchamber x1, opencode x1, nix x1; created 20:40:00-01Z) ended failed. The 15:00 and 15:20 waves on the same route succeeded, so this began between 15:20 and 15:40 CDT.

Error (verbatim, all 4 sampled sessions identical)

{"type":"provider.auth","status":401,"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."}

Sampled session ids: ses_e5e6c4f2b7effeHIO4alcU5YIq (code), ses_e5e6c4f2f2bffe1i6T5JblGWKv (nix), ses_e5e6c4f312bffeYDe72XVmZ14C (openchamber), ses_e5e6c4f323dffeON6AESTsjITW (opencode). Zero thought_signature hits — this is NOT the antigravity invalid-request pattern from the 12:00-13:00 waves.

Context

  • The codex / openai.alternate pool (popesdrew@gmail.com) read 5h 100% (weekly 53%) in the same cycle's quota snapshot, and the second credential (drewrypope@gmail.com) weekly 100%. The failing leg appears to be an OpenAI credential whose token/key lacks the api.responses.write scope (restricted key or Reader role).
  • Until this is fixed, workhorse-route lane sessions that fall through to this credential die on their first model call; queue work stalls behind claim expiry.

Acceptance

  • Identify which credential/leg of the workhorse route returns the 401 and why its scope/role changed.
  • Restore a working leg (scope/role fix or route the leg to a credential with api.responses.write) — plan quotas only, never PAYG.
  • Verify with a fresh lane wave: roots on opencode-route/workhorse complete model calls without provider.auth errors.

Activity

  1. added
    kind/infrastructureInfrastructure / host-portability work
    priority/highImportant and time-sensitive
    effort/mMedium: expected under 1 day of work
    user/reviewNeeds a user-look: Muse reviews first and handles what it can
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 1, 2026
  2. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Intake review

    Related: #3256 (the standalone-proxy endpoint defect in front of this one — distinct; both must be fixed before a GPT leaf serves through the proxy).

    Notes: The failure window is pinned by the conductor's cycle evidence: 15:00 and 15:20 CDT waves on opencode-route/workhorse succeeded, the 15:40 wave failed 9/9 roots with an identical provider.auth 401 (Missing scopes: api.responses.write), sampled across all four repos. The body already carries acceptance criteria, so no body change was made. Added user/review: identifying and re-scoping/re-roling an OpenAI credential may need an org-admin decision only Drew can make — Muse review lane first, per the taxonomy's user/ flow. Plan quotas only, never PAYG, per the standing pool rules.

    Risks: Until a working leg is restored, workhorse-route sessions that fall through to this credential die on their first model call and queue work stalls behind claim expiry — this is a fleet-throughput blocker, not a single-app bug.

    Suggested approach: Identify the credential from the failing legs' route_attempt/session records, check its key scopes and org/project role against api.responses.write, then restore the scope/role or re-point the leg at a credential that has it, and verify with a fresh lane wave per the body's acceptance list.

    — fleet monitor intake, 2026-10-31 fleet-monitor run (19:12 CDT)

  3. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Root cause — fixed and verified 2026-10-31 ~19:53 CDT

    The failure was a credential/method mismatch in the OpenCode credential store, not quota and not the account.

    The fork ships two OpenAI sign-in paths that wire provider openai to different backends:

    • chatgpt-browser / chatgpt-headless (openai.ts plugin) → Codex backend https://chatgpt.com/backend-api/codex, token = ChatGPT plan token from the Codex client (app_EMoamEEZ73f0CkXaXp7hrann).
    • chatgpt-token-sharing (chatgpt.ts plugin) → platform API https://api.openai.com/v1, which requires a token minted by that flow for the registered agent client, carrying resource.invoke + chatgpt.tokens.use.direct scopes.

    For the main account (drewrypope) the store held both credentials. The token-sharing credential was the active one, but the access token stored under it is a Codex-flow token: claims show client_id app_EMoamEEZ73f0CkXaXp7hrann and scp [openid, profile, email, offline_access, api.connectors.read, api.connectors.invoke] — byte-identical (same jti/iat) to the Muse's current token in ~/.codex/auth.json. It was never minted by the token-sharing flow, so when the plugin sent it to api.openai.com/v1/responses, the platform API rejected it: 401 "Missing scopes: api.responses.write". A refresh could not repair it — refresh re-mints from the original grant, which never included the token-sharing scopes, and the stored refresh token belongs to the Codex client, not the agent client named in the credential metadata.

    Timing matches this issue's window: the chatgpt-browser credential row was last updated at 15:40 CDT (the active-path flip), right when the waves started failing; the 15:00/15:20 waves succeeded on the Codex-backend path. Quota is excluded: after the main account's quota reset (weekly at 1% on the 19:30 hub read), attempts at 19:40–19:49 CDT still failed 401.

    Fix

    opencode auth switch openai <chatgpt-browser credential id> — reactivated the existing browser credential for the main account (same account, same tokens, Codex-backend wiring; its metadata carries the accountID used for the chatgpt-account-id header) and deactivated the token-sharing credential. No config edits, no token changes, no re-login required. The token-sharing row remains stored but inactive. Note: the chatgpt plugin auto-migrates browser credentials to token-sharing only when their metadata carries a clientID; this credential's metadata is accountID-only, and its method was verified unchanged after the switch and plugin reload.

    Verification (route_attempt ledger)

    • Through 19:49:59 CDT: repeated failures, provider openai, model gpt-6.1-terra, route workhorse, 401 Authentication.
    • From 19:53:26 CDT: 9 consecutive successes (ledger ids 42465–42473), provider openai / gpt-6.1-terra / workhorse, from the same fleet sessions that had been 401ing. Zero openai failures since the switch.

    Alternate account

    openai.alternate (popesdrew) is method chatgpt-headless — already on the Codex-backend path, not affected by this defect; left untouched.

    #3256 (standalone proxy endpoint config) is distinct and still open — it gates GPT through the proxy door (e.g. OpenClaw), not the internal routing fixed here.

    (Editorial note: this comment replaces an earlier rendering whose inline-code spans were stripped by a shell-quoting accident during posting; substance unchanged.)

  4. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Follow-up (2026-10-31 ~20:11–20:25 CDT) — recurrence guard ordered by Drew, both parts done:

    1. Stray credential deleted. The inactive chatgpt-token-sharing credential for provider openai was removed via opencode auth logout openai <id>:

    • credential id cred_10b633957001YZ1Wclg9xbOEXM, label "OpenAI 2", method chatgpt-token-sharing, account 0227321a-a7f6-4f8c-8ce8-3c1c12dda014 (drewrypope main), created 2026-10-05, active=0 at deletion. (Its token was the Codex-flow token still live in the Muse's own auth store, so no secret was lost to the machine.)
    • Post-delete store state (verified twice, independently): provider openai holds exactly one credential — cred_10b5fd438001thsQzMXLQMc9oD, method chatgpt-browser, active. openai.alternate untouched (headless, active).
    • GPT still works: route ledger after 20:13 CDT shows 215 successes / 3 failures for provider openai; all 3 failures are RateLimit/429 — zero Authentication/401. The missing-scopes failure mode is gone.
    • Recreation watch (5 min post-deletion): the token-sharing row did not reappear.

    2. Watchdog added. tools/openai-cred-watchdog.py in dezren39/hatch (commit eafdee9), run read-only against the laptop credential store. Invariant: for openai — no chatgpt-token-sharing row may exist, and the active method must be chatgpt-browser/chatgpt-headless; for openai.alternate — active method must be browser/headless. Prints OK + exit 0 when healthy; VIOLATION lines naming credential id/method + exit 1 otherwise. Never prints token values.

    • Teeth test (against mutated COPIES of the store in /tmp; live store never mutated): (a) inactive token-sharing row re-inserted → flagged; (b) the exact 15:40 failure shape (token-sharing row active, browser inactive) → flagged twice; (c) alternate's active method flipped to token-sharing → flagged. Healthy live store → OK, exit 0.
    • Scheduling: to run hourly via a Muse cron from the main session (silent when healthy; VIOLATION output surfaces to Drew in chat). A laptop-native systemd unit was not hand-added because all laptop user units are Nix/home-manager managed from the code flake; a flake-defined unit remains the durable alternative if preferred.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

effort/mMedium: expected under 1 day of workkind/infrastructureInfrastructure / host-portability workmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivestatus/needs-triageNot yet triaged; needs a first lookuser/reviewNeeds a user-look: Muse reviews first and handles what it can

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions