Kiro: dead refresh token (400 invalid_request) never flags needsReauth — dashboard keeps "Logged in" with no re-auth button
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- Half a day
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- authentication
Research direction
Start in src/oauth/kiro.ts at KIRO_TERMINAL_REFRESH_ERRORS, then read terminal() and publicOAuthAuthenticationErrorMessage() in src/oauth/index.ts to see why a 400 invalid_request never marks the account. Add the error classification so a dead Kiro refresh token sets needsReauth (or maps to the verify_account reauth reason), and check the provider overview rendering driven by GET /api/oauth/status so an expired expiresAt is not shown as green "Logged in". Done when replaying the 400 invalid_request refresh flips the dashboard to "Needs attention" with a Re-authenticate button; locate the existing terminal refresh-error tests first and run them.
Written by the indexing model from the issue text.
Description
Client or integration
OpenCodex dashboard
Area
Authentication and account pool
Summary
When a Kiro refresh token becomes irredeemable (AWS SSO OIDC no longer accepts it), the account is never flagged needsReauth. The dashboard keeps showing Kiro as Ready / Connected / Logged in with no Re-authenticate button, and every request fails 401 with the generic fallback message. I expected the provider card to flip to "Needs attention" with the Re-authenticate button (or at least the specific "Not logged in to kiro. Run: ocx login kiro" message), like the terminal refresh-error path already produces.
Likely root cause (read from the installed 2.78.0 package):
- KIRO_TERMINAL_REFRESH_ERRORS in
src/oauth/kiro.tscovers only invalid_grant, refresh_token_reused, revoked, revoked_token, refresh_token_revoked, access_denied, expired_token. - The dead credential gets a consistent HTTP 400 from oidc..amazonaws.com/token with not in that set.
{"error":"invalid_request","error_description":"Invalid request","location":null,"reason":null} - terminal() in
src/oauth/index.tstreats KiroTokenRefreshError as terminal only when a 400/401 carries one of the known oauthError values, so the account is never marked; the raw error then surfaces through publicOAuthAuthenticationErrorMessage() as the generic "OAuth authentication failed." text.
Evidence:
-
Stored credential expired at 2026-10-06T10:50:38Z; tested at 2026-10-07T03:34Z.
-
GET /api/oauth/status?provider=kiro still returns loggedIn: true with that expired expiresAt and no needsReauth flag.
-
Client error on every request: "unexpected status 401 Unauthorized: OAuth authentication failed. Check the OpenCodex account status and retry."
-
Replaying the exact refresh request the proxy sends (same registered clientId/clientSecret, same refreshToken) returns HTTP 400 invalid_request.
-
The stored refresh token is identical to the local kiro-cli session token, whose access token is also expired. Only a fresh browser login restores access; no refresh path can.
Reproduction
- Import/complete a Kiro login (kiro-cli device/Builder ID flow), then let both the access token and the refresh grant go stale (e.g. leave it idle for a day).
- Send any request routed to the kiro provider, e.g. POST /v1/responses with model: kiro/.
- Observe the 401 generic OAuth message; dashboard Providers -> Kiro still shows Connected / Logged in with no Re-authenticate button, and Current account usage shows "Failed to refresh quotas".
- Manually POST the same refresh payload (grantType: refresh_token, clientId, clientSecret, refreshToken) to
https://oidc.<region>.amazonaws.com/tokenand observe 400 invalid_request.
Version
2.78.0
Operating system
Ubuntu 26.04.1 LTS
Provider and model
kiro / claude-opus-5.5 (any Kiro model; the failure happens at the auth/token-resolution stage)
Logs or error output
[opencodex] OAuth refresh started provider=kiro account=account-…55fe
That line repeats roughly 40 times over an hour (request failures and quota probes keep re-attempting refresh); the credential never rotates and the account is never flagged.
ocx logs --provider kiro --limit 3 --json (redacted row fields):
{
"status": 401,
"durationMs": 40,
"errorCode": "invalid_api_key",
"failureStage": "headers-only",
"failureCause": "credential-rejected",
"resendPermission": "permitted-after-repair",
"upstreamError": "OAuth authentication failed. Check the OpenCodex account status and retry."
}
AWS token endpoint replay (same registered client + stored refresh token as the proxy uses):
HTTP 400
{"error":"invalid_request","error_description":"Invalid request","location":null,"reason":null}
Screenshots and supporting files
Dashboard screenshot showing Kiro "Connected / Logged in" plus "Failed to refresh quotas" while all Kiro requests 401; client screenshot of the generic 401 error.
Redacted configuration
{
"providers": {
"kiro": {
"adapter": "kiro",
"authMode": "oauth",
"baseUrl": "https://runtime.us-east-1.kiro.dev",
"defaultModel": "kiro-auto"
}
},
"tokenGuardian": null
}
Note: tokenGuardian is unset (defaults off) and Kiro's default refresh policy is lazy-only, so nothing proactively probes. But even a manual request never sets needsReauth either, because the 400 invalid_request response is not classified as terminal. A UI-level fix would also work: /api/oauth/status already returns expiresAt, and still renders a green "Logged in" for tokens that expired a day+ ago.
Suggested fix (either or both): treat the Kiro SSO 400 invalid_request refresh rejection as terminal (or map it to the verify_account reauth reason), and/or render token-expired state from expiresAt in the provider overview instead of a permanent healthy "Logged in".
Redacted configuration
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
- Dominant language
- TypeScript
- Stars
- 16.9k
- Forks
- 1.3k
- Avg merge
- 4h 52m
- Merged PRs (30d)
- 609
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from lidge-jun/opencodex
-
account-pool enhancement proxy
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
[Bug]: Native Messages lane never logs requestedEffort for Claude Code requests (re-file of #5453)Openbug proxy
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 1 day
-
account-pool bug
Difficulty 4/5 3-5 days Newbie friendliness 48/100
lidge-jun/opencodex#6749 · 1 comment ·
Maintainers usually reply within 1 day
-
bug platform
Difficulty 5/5 Over a week Newbie friendliness 30/100
Maintainers usually reply within 1 day
All issues in lidge-jun/opencodex
Similar issues
-
[bug] diagnostics.dumpBody:Buffer 形态请求(透传 lane)跳过 dumps/ 落盘,仅留 raw/-unknown-Possibly taken @ranxianglei claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
ranxianglei/billion-context#2421 · 2 comments ·
Maintainers usually reply within 1 day
-
pending triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
nuxt/test-utils#1842 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
MoonshotAI/kimi-code#4146 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
farbenmeer/tapi#531 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day