Skip to content

fix(agent): classify Anthropic "extra usage" 400 as billing, not format_error - #49379

Closed
aldoeliacim wants to merge 1 commit into
NousResearch:mainfrom
aldoeliacim:fix/anthropic-extra-usage-400-billing
Closed

fix(agent): classify Anthropic "extra usage" 400 as billing, not format_error#49379
aldoeliacim wants to merge 1 commit into
NousResearch:mainfrom
aldoeliacim:fix/anthropic-extra-usage-400-billing

Conversation

@aldoeliacim

@aldoeliacim aldoeliacim commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

What & why

When a Claude subscription must enable extra usage to keep serving, native Anthropic returns HTTP 400 with a body like:

Third-party apps now draw from your extra usage. To continue, enable extra usage in your account settings.

_classify_400 had no pattern for this, so it fell through to the generic format_error bucket. Two real consequences:

  1. Wrong user guidance — the user sees an opaque "format error" instead of the actionable billing/entitlement guidance (_print_billing_or_entitlement_guidance).
  2. No credential rotationbilling sets should_rotate_credential=True; format_error does not. A multi-credential pool never rotates to another entry on this signal.

Both _classify_400 (status path) and _classify_by_message (no-status sibling path) are fixed, so the whole class is covered rather than one call site.

The fix

Add a dedicated _ENTITLEMENT_PATTERNS list and classify these messages as FailoverReason.billing (retryable=False, should_rotate_credential=True, should_fallback=True).

Patterns are deliberately specific to an account action on extra usage (draw from your extra usage, enable extra usage, exceed your extra usage, extra usage allowance) so they do not collide with the existing 429 rolling-cap / long-context tier gate ("extra usage" + "long context"long_context_tier, retryable). The no-status path additionally guards on "long context" explicitly.

How to test

from agent.error_classifier import classify_api_error, FailoverReason
# build a 400 whose body message is:
#   "Third-party apps now draw from your extra usage. ..."
# classify_api_error(...).reason  -> FailoverReason.billing (was format_error)
# .should_rotate_credential       -> True (was False)

New test tests/agent/test_entitlement_extra_usage_classification.py covers:

  • three entitlement phrasings → billing + rotate + fallback
  • no-collision invariant: 429 extra usage + long context stays long_context_tier
  • control: real credit balance billing still billing

tests/agent/test_error_classifier.py (161 tests) stays green.

Platforms

Logic-only change in agent/error_classifier.py (string matching + classification flags); no OS-specific paths. Tested on Linux.


Rebased onto current main (2026-06-26). Conflict in agent/error_classifier.py: main added an _OVERLOADED_PATTERNS block at the same no-status classification point; resolved by ordering overloaded → entitlement → generic billing so the transient-overload check runs first and the specific entitlement match still wins over generic _BILLING_PATTERNS. Verified: test_error_classifier + entitlement + failover = 180 passed.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/anthropic Anthropic native Messages API P2 Medium — degraded but workaround exists labels Jun 20, 2026
@aldoeliacim
aldoeliacim force-pushed the fix/anthropic-extra-usage-400-billing branch 3 times, most recently from 6bc8ecf to 1a83bfc Compare July 1, 2026 20:44
Copilot AI review requested due to automatic review settings July 1, 2026 20:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Hermes’ API error classification to recognize Anthropic “extra usage” entitlement errors that arrive as HTTP 400 and route them to the billing failover bucket (instead of format_error), enabling correct user guidance and credential rotation behavior.

Changes:

  • Add _ENTITLEMENT_PATTERNS and classify matching Anthropic 400 errors as FailoverReason.billing (non-retryable, rotate credential, allow fallback).
  • Extend the no-status message-only classifier to also treat these entitlement messages as billing, while explicitly avoiding collision with the 429 long-context tier gate.
  • Add a focused test suite covering entitlement phrasings and the “no collision” invariant with the existing 429 long_context_tier behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
agent/error_classifier.py Adds entitlement-specific pattern matching to classify certain Anthropic 400s (and message-only cases) as billing.
tests/agent/test_entitlement_extra_usage_classification.py Adds regression tests for entitlement message classification and guards against misclassifying the 429 long-context tier gate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@aldoeliacim
aldoeliacim force-pushed the fix/anthropic-extra-usage-400-billing branch from 1a83bfc to 2881e94 Compare July 3, 2026 00:15
@aldoeliacim
aldoeliacim force-pushed the fix/anthropic-extra-usage-400-billing branch from 2881e94 to 73f7bd5 Compare July 4, 2026 19:58
…at_error

When a Claude subscription must enable extra usage to keep serving, native
Anthropic returns HTTP 400 with a body like "Third-party apps now draw from
your extra usage. To continue, enable extra usage in your account settings."
The generic 400 path classified this as format_error, so:

  - the user saw an opaque "format error" instead of the actionable
    billing/entitlement guidance, and
  - no credential rotation was triggered (should_rotate_credential stayed
    False), so a multi-credential pool never rotated to another entry.

Add a dedicated _ENTITLEMENT_PATTERNS list and classify these messages as
FailoverReason.billing (retryable=False, should_rotate_credential=True,
should_fallback=True) in both the 400-status path and the no-status message
path. Patterns are specific to an account *action* on extra usage so they do
NOT collide with the 429 rolling-cap / long-context tier gate ("extra usage" +
"long context"), which stays long_context_tier; the no-status path also guards
on "long context" explicitly.

Tests: entitlement variants -> billing + rotate; long-context tier stays
long_context_tier; real credit-balance billing unchanged. Full classifier
suite (161) green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants