mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
An expired app-password token could wedge an account permanently. The 401 branch clears the cached token, but some PDSes report the same condition as 400 with an atproto error name in the body, which fell through to the generic non-200 branch. That clears only the derived service token, so the dead bearer token stayed in the cache and every subsequent request replayed it. Observed on one account against at.hexlab.foo: 16,110 of these errors and 4,254 retryable 503s over 33 hours, with no recovery path. The cache is in-memory, so it only cleared on process restart. Now the non-200 branch classifies the atproto error name and evicts on the ones that mean the presented token is unusable, matching what the 401 branch already does. For app-passwords that is the equivalent of a refresh: the next authentication re-mints via createSession. Deliberately not routed through oauth.IsSessionInvalidError, which excludes ExpiredToken on purpose — there it would delete a recoverable OAuth session and sign the user out everywhere, whereas here the only thing discarded is a cache entry that will be repopulated. Not addressed here: the failure still surfaces as a 503, which is retryable and so keeps clients looping. Returning 401 with the re-auth hint would be the better signal, but it spans the token handler and is a separate change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>