close
Skip to content

fix(acp): classify expired sessions as auth, not transient 5xx - #1984

Merged
iamwhatever merged 1 commit into
mainfrom
fix/critical-bugs-1940-1835-1942
Aug 7, 2026
Merged

fix(acp): classify expired sessions as auth, not transient 5xx#1984
iamwhatever merged 1 commit into
mainfrom
fix/critical-bugs-1940-1835-1942

Conversation

@iamwhatever

Copy link
Copy Markdown
Collaborator

Problem / Motivation

When a session expires while the dashboard is open, every subsequent action fails behind a misleading banner:

❌ The model backend hit a transient error (HTTP 5xx). This is usually momentary — retry in a moment. If it keeps happening, switch to a different model in the picker.

The UI still looks functional — modals open, Allow responds, the model picker works — but nothing goes through, and the message points at two fixes that cannot possibly work. In the reported case the user stepped away for a couple of minutes, came back to an approval modal, clicked Allow, saw nothing happen, then repeatedly re-asked and swapped models on the app's own advice before realising the session had simply expired.

Root cause

An expired session is rejected with 401/403, and nothing in the ACP error classifier recognised those codes:

  • _RE_AUTH matches only Bedrock's named exceptions (AccessDeniedException, ExpiredTokenException, …) — a bare status code carries none of them.
  • _RE_5XX_STATUS matches 5xx only.

The aborted request leaves a transport error (DispatchFailure / ConnectionResetError) next to the rejection, and the 5xx family matched that first. So the failure was classified transient: the user got retry advice, and _is_transient_raw_error re-armed the retry ladder on an error no retry can clear.

What changed

  • _RE_AUTH_STATUS — detects HTTP 401/403. This is the primary signal, because the rejection arrives with no explanatory wording.
  • _RE_SESSION_EXPIRED — prose fallback (session expired, not logged in, login required, …) for backends that describe the expiry in words instead of a code.
  • _is_session_expired() — both signals behind one helper, used by both the message formatter and the retry classifier, so the two cannot drift (the same drift hazard the surrounding comments already warn about).
  • Evaluated after _RE_AUTH and before the 5xx family, so a transport error from the aborted request no longer shadows the real cause.
  • The message now names the remedy and closes off the dead ends: "Run kiro-cli login … Retrying or switching models will not help — this is a sign-in issue, not a backend error." Wording matches the existing _NOT_LOGGED_IN_MESSAGE.

Testing

473 passed in test/test_acp_client.py; 269 passed across test_acp_session_provider.py + test_acp_runtime.py (no regressions). isort / flake8 / mypy clean.

Six new/extended tests, mutation-verified against pristine main — all six fail without the change:

Test Covers
test_session_expired_by_http_status bare HTTP 401 / 403 / status code 401
test_session_expired_401_with_transport_error the reported failure mode — 401 + DispatchFailure ConnectionResetError
test_session_expired_rewrite prose expiry gets sign-in guidance, not 5xx advice
test_session_expired_variants 7 wording variants
test_session_expired_with_5xx_token_wins ordering: expiry beats 5xx
test_session_expired_is_not_transient retry classifier treats expiry as terminal

test_genuine_5xx_still_transient_with_auth_absent is the overreach guard — it passes on main as well as here, confirming real 5xx errors stay retryable and the new branch is not swallowing them.

Scope

Closes the misclassification half of the issue (item 1). The issue also asks for a "Sign back in" affordance in the UI (item 2) — that is a frontend change and is deliberately not in this PR; the corrected backend message is what unblocks the user today. Filing that separately keeps this diff reviewable.

The branch name mentions #1940 and #1835 — both turned out to be already handled (#1940 merged via #1941, #1835 open as #1841), so this PR contains only the #1942 fix.

An expired session is rejected with 401/403, but nothing in the ACP error
classifier recognised those codes. The aborted request leaves a transport
error (DispatchFailure / ConnectionResetError) alongside the rejection,
which matched the 5xx family first, so the user was told "transient error
— retry in a moment, or switch to a different model". Neither can succeed
against an expired login, and the retry ladder burned attempts on it.

Adds 401/403 status detection as the primary signal, plus a prose fallback
for backends that describe the expiry in words, and routes both through one
helper so the message formatter and the retry classifier cannot drift. The
message now names the actual remedy and says outright that retrying and
model-switching will not help.

Genuine 5xx errors stay retryable — covered by a guard test.
@iamwhatever
iamwhatever requested a review from a team as a code owner August 7, 2026 06:59
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Advisory design-level review of ef3e753f428309cc9915dfb99c372de2a9da055a — updated in place on each push; does not block merge.

Design-Verdict: PASS

Extends the existing prose-classifier taxonomy in its established shape — shared helper prevents formatter/retry drift, ordering fix targets the actual shadowing root cause.

Suggestions

  • 403 also arrives on genuine authorization denials (org policy, model access) where kiro-cli login won't help; hedging the message ("expired or lacks permission") avoids re-creating a smaller version of the wrong-remedy bug this PR fixes.

[DESIGN-REVIEWED] ef3e753

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of ef3e753f428309cc9915dfb99c372de2a9da055a and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] ef3e753

False positive or not applicable? A repository writer can comment:
/ai-review override gpt ef3e753f428309cc9915dfb99c372de2a9da055a: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Opus 5 Review — ✅ no blocking findings

Reviewed ef3e753f428309cc9915dfb99c372de2a9da055a — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] ef3e753

Verdict parsed from the review's SHA-scoped output markers for commit ef3e753f428309cc9915dfb99c372de2a9da055a.

False positive or not applicable? A repository writer can comment:
/ai-review override fable ef3e753f428309cc9915dfb99c372de2a9da055a: <one-sentence reason>

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 7, 2026
@iamwhatever
iamwhatever merged commit 71b3877 into main Aug 7, 2026
47 checks passed
@iamwhatever
iamwhatever deleted the fix/critical-bugs-1940-1835-1942 branch August 7, 2026 07:22
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 7, 2026
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…otdev#1984)

An expired session is rejected with 401/403, but nothing in the ACP error
classifier recognised those codes. The aborted request leaves a transport
error (DispatchFailure / ConnectionResetError) alongside the rejection,
which matched the 5xx family first, so the user was told "transient error
— retry in a moment, or switch to a different model". Neither can succeed
against an expired login, and the retry ladder burned attempts on it.

Adds 401/403 status detection as the primary signal, plus a prose fallback
for backends that describe the expiry in words, and routes both through one
helper so the message formatter and the retry classifier cannot drift. The
message now names the actual remedy and says outright that retrying and
model-switching will not help.

Genuine 5xx errors stay retryable — covered by a guard test.

Co-authored-by: zejiangg <zejiangg@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants