fix(dashboard): credits panel fails fast on API-key auth (#5728) - #5750
Conversation
API-key accounts hold no SSO/OIDC bearer token, so the usage refresh spent
~30s inside fetch_usage_limits walking credential stores that cannot contain
one, and with the text scrape disabled no EXPLANATORY terminal state ever
reached the frontend: the credits panel spun through the timeout and then hid
itself with no explanation, every refresh.
Backend: right after the whoami identity read, an account_type of ApiKey
(kiro-cli's AuthMethod enum variant; the compare normalizes case and strips
separators so an upstream respelling still fails fast) publishes the existing
unavailable-marker shape with a reason
({available: false, reason: 'api_key_auth'}) and returns before the
credential search and the billed scrape.
Frontend: the reasoned marker becomes a terminal 'api-key' state — the
top-bar segment renders the same dash as 'failed' with a label that says why,
and the account modal explains it instead of spinning or claiming a generic
failure. New i18n keys across all 13 catalogs. Reasonless available:false
keeps its existing hide-the-pill behavior.
Closes #5728
UX Review (Fable 5) — ✅ PASSUX-level review of UX-Verdict: PASS A permanent state that previously spun forever now says exactly why it's terminal, in plain words at both the pill and the modal. The new strings pass the cold read ("Credit usage isn't available for API key authentication" asserts the held state, no hedging), the pill reuses the established terminal-dash pattern from [UX-REVIEWED] 473c608 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of All the code checks out — the fix is at the true root cause (the usage API fundamentally requires an SSO/OIDC token that API-key auth never has), the Design-Verdict: PASS Fail-fast at the auth-type root cause, additive marker contract, and every miss degrades to the pre-existing behavior — sound and proportionate. [DESIGN-REVIEWED] 473c608 |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of I have everything I need. Let me run the final checks on my counts before emitting: First-Principles-Verdict: PASS A reported defect (#5728), fixed where the auth type is first knowable, riding an existing marker shape — every item declared and consumed. What this change shipsIntent: stop API-key-authenticated accounts' credits panel from spinning forever, and tell them why usage is unavailable — a FIX.
No undeclared items, no riders, no duplicated mechanism, no zero-consumer surface. The scope guard the issue demanded (leave [FIRST-PRINCIPLES-REVIEWED] 473c608 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsThe discovery pass found no candidates. The backend No findings. [OPUS-REVIEWED] 473c608 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
bolichen97
left a comment
There was a problem hiding this comment.
Approving per triage sweep: readiness passed, no blocking reviews, fix-type change. Auto-merge will be enabled; branch protection still gates.
Open PR relationship auditThis is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion. Relationship findings
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
Summary
Closes #5728
On accounts authenticated with an API key, the dashboard's Kiro Credits panel showed a warming spinner while the backend usage refresh spent its timeout inside
fetch_usage_limitssearching for SSO/OIDC bearer tokens that this auth type never has; with the text-scrape fallback disabled (the production default) no explanatory terminal state ever reached the frontend — the panel spun through the timeout, then the pill silently hid, and every 30s refresh repeated the cycle.Backend
In
_refresh(src/kiro_crew/dashboard/handlers/sessions.py), right after the_fetch_whoamiidentity read: whenaccount_typeis kiro-cli'sApiKeyAuthMethod variant (the compare normalizes case and strips separators, so an upstream respelling likeAPI_KEYstill fails fast instead of silently regressing to the slow path), publish the existing unavailable-marker shape with a reason —{"available": false, "reason": "api_key_auth"}— log at info, and return before the credential search and the billed scrape.Frontend
The reasoned marker becomes a new terminal
'api-key'state:'failed', with a label that says why (app.kiro_credit_usage_api_key);components.kiroAccountModal.credit_usage_api_key_auth) instead of spinning or showing the generic unavailable line;available: false(non-Kiro provider) keeps its existing hide-the-pill behavior.New i18n keys across all 13 authored catalogs + regenerated
en-XA.Scope guard (per the issue's fix contract)
The text-scrape fallback,
fetch_usage_limitscredential-search behavior for SSO accounts, and_identity_matches_accountare untouched.Screenshots
The existing
capture-credit-pill-states.mjsharness gained anapi-keyscenario (real built SPA, stubbed network; the harness self-checks the segment's aria-label at capture time).Account modal in the new state:
Testing
TestApiKeyAuthFailFast(6 tests) — short-circuit beforefetch_usage_limits+ exact marker shape; respelling-tolerant compare parametrized overapikey/APIKEY/" ApiKey "/API_KEY/Api-Key; no billed scrape spawned even with the scrape opted in; SSO negative control still reaches the API. Mutation-verified: disabling the fail-fast branch turns 5 of 6 red (the negative control stays green by design). Fulltest/test_session_usage.py: 93 passed.api_key_authpayload as terminal (dash + explanatory label, no spinner) and the reasonless payload as still-hidden; modal test pins the specific message over the generic one. 17 passed across both files;npx tsc -bclean.i18n:check/lint:i18nall green locally.Pre-push review
Two model-pinned lanes reviewed the commit diff before opening this PR: GPT (gpt-5.6-sol, codex-review contract) — PASS, no findings; Opus (claude-opus-5, claude-review + AUTOSDE contract) — PASS, 0 blocking, 4 advisory. Adopted: A1 (docstring/commit-message overclaim reworded), A3 (respelling-tolerant compare + parametrize), A4 (i18n key order in translation catalogs). A2 (surfacing the whoami identity beside the marker so the modal can show the account email) is a two-sided change beyond this fix's minimal surface and is left as a follow-up.