mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix(xai-oauth): apply WKE disambiguator to recovery-path catch-all (#29344)
_recover_with_credential_pool had a second classification site that blanket- treated any 403 against xai-oauth as entitlement (defense-in-depth for #26847). That override defeated the new _is_entitlement_failure disambiguator from the parent commit — bad-credentials 403s still short-circuited the refresh path. Apply the same WKE-unauthenticated / OAuth2-validation-phrase guard at the override site so xAI's authoritative 'this is auth, not entitlement' signal wins there too. The #26847 catch-all still triggers for genuine entitlement bodies that don't carry the disambiguator. Closes the end-to-end gap exposed by test_recover_with_credential_pool_refreshes_on_xai_bad_credentials_403.
This commit is contained in:
parent
b5ea6a5c80
commit
cc93053b42
1 changed files with 20 additions and 1 deletions
|
|
@ -617,9 +617,28 @@ def recover_with_credential_pool(
|
|||
# existing entitlement keyword set in ``_is_entitlement_failure``.
|
||||
# Any 403 against ``xai-oauth`` is treated as entitlement here so
|
||||
# the refresh loop can't spin in those cases either.
|
||||
#
|
||||
# Exception (#29344): xAI's ``[WKE=unauthenticated:...]`` suffix and
|
||||
# the ``OAuth2 access token could not be validated`` phrasing are
|
||||
# xAI's authoritative "this is a stale token, not entitlement"
|
||||
# signal. When either fires we must NOT apply the catch-all
|
||||
# override — refresh is the recoverable path for these bodies, and
|
||||
# blanket-classifying them as entitlement was the bug that left
|
||||
# long-running TUI sessions stuck on stale tokens until the user
|
||||
# exited and reopened.
|
||||
is_entitlement = agent._is_entitlement_failure(error_context, status_code)
|
||||
if not is_entitlement and status_code == 403 and (agent.provider or "") == "xai-oauth":
|
||||
is_entitlement = True
|
||||
_disambiguator_haystack = " ".join(
|
||||
str(error_context.get(k) or "").lower()
|
||||
for k in ("message", "reason", "code", "error")
|
||||
if isinstance(error_context, dict)
|
||||
)
|
||||
_is_xai_auth_failure = (
|
||||
"[wke=unauthenticated:" in _disambiguator_haystack
|
||||
or "oauth2 access token could not be validated" in _disambiguator_haystack
|
||||
)
|
||||
if not _is_xai_auth_failure:
|
||||
is_entitlement = True
|
||||
if is_entitlement:
|
||||
_ra().logger.info(
|
||||
"Credential %s — entitlement-shaped 403 from %s; "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue