From 77763f00fb9ecaf7ee7d4cca7b8c066dfc927dba Mon Sep 17 00:00:00 2001 From: Manuel Guttmann Date: Mon, 13 Jul 2026 13:24:24 +0200 Subject: [PATCH] fix(auth): normalize Anthropic sk-ant-oat pool creds to OAuth Manually-added Anthropic setup-tokens defaulted to api_key and were sent via x-api-key, which Anthropic rejects -> 429. Infer OAuth from the sk-ant-oat prefix in from_dict so all ingest paths agree with _is_oauth_token(). Fixes #63737. --- agent/credential_pool.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/agent/credential_pool.py b/agent/credential_pool.py index 6b36bf4554f8..566b6d6f6702 100644 --- a/agent/credential_pool.py +++ b/agent/credential_pool.py @@ -175,6 +175,16 @@ class PooledCredential: data.setdefault("id", uuid.uuid4().hex[:6]) data.setdefault("label", payload.get("source", provider)) data.setdefault("auth_type", AUTH_TYPE_API_KEY) + # An Anthropic setup-token (sk-ant-oat*) is always OAuth regardless of + # how it was ingested. The env path infers this, but manually-added + # credentials (hermes auth add / dashboard) default to api_key and are + # then sent with an x-api-key header, which Anthropic rejects for OAuth + # tokens. Normalize here so every ingest path agrees with + # _is_oauth_token()'s prefix detection. See issue #63737. + if provider == "anthropic": + _oat = data.get("access_token") or "" + if isinstance(_oat, str) and _oat.startswith("sk-ant-oat"): + data["auth_type"] = AUTH_TYPE_OAUTH data.setdefault("priority", 0) data.setdefault("source", SOURCE_MANUAL) data.setdefault("access_token", "")