Treat bare Anthropic 403 as transient, not a credential failure - #5041
builder-io-integration[bot] wants to merge 2 commits into
Conversation
…ejected credential The Anthropic engine tagged every provider HTTP status `http_<status>`, so an empty-body "403 status code (no body)" ended the turn on its first occurrence, discarded the partial answer, and routed a working credential onto the "reconnect your provider" lane. The Builder gateway engine and the AI SDK lane already classify that exact wording as a transient rejection; the Anthropic engine now shares the same predicate, so the run retries with backoff and a turn that stays refused reports a retryable sentence instead of a bare HTTP status echo. A 403 that carries a real reason keeps `http_403`.
|
@builderio-bot look at the latest PR feedback and fix anything you agree with. Be skeptical. Reply in each open inline thread with exactly one of:
Or resolve the thread in GitHub. Outdated threads after new commits do not need a new reply. Get CI green and keep the branch mergeable. |
…1a033f8ba1354246a7ad
There was a problem hiding this comment.
Builder reviewed your changes — looks good ✅
Review Details
Incremental Code Review Summary
I reviewed the current PR #5041 head after the update. The changed scope remains the Anthropic engine's handling of reasonless 403 responses, the shared retry/continuation classification, parity regression coverage, and the core changeset. The implementation still classifies an empty-reason 403 as transient and retryable while preserving credential-lane behavior for reasoned 403 responses and explicit non-retryable SDK verdicts.
This remains a standard-risk core behavior change. Two independent balanced incremental reviews found no new actionable issues, and the relevant parity plus Anthropic/error-classification suites passed. There were no previous inline findings to resolve or avoid resubmitting. The current diff has no user-interface changes.
🧪 Browser testing: Skipped — PR only modifies core provider/error handling, tests, and release metadata; no browser/UI surface changed.
Summary
Fixes the Anthropic engine so a reasonless (empty-body) 403 response is classified as transient provider load-shedding and retried, instead of ending the turn and telling the user to reconnect a working provider credential.
Problem
Factory item
58ff04c94d0fe98b4ead1bbc7925a3d3425a71e5ae5cb6a79aff792ba4750ca5(source Slack thread: https://slack.com/app_redirect?team=T0GCV21GE&channel=C0ATH3CCZT4&message_ts=1789455253.090849) reported that generating an interactive wireframe in the Plan app hung on "Working" for ~90 seconds, then crashed with a bare toast: "The agent hit an error — 403 status code (no body)" (code: http_403), aborting the whole generation with no partial output and no actionable message.Root cause: the Anthropic engine tagged every HTTP status as
errorCode: http_<status>regardless of context. A403 status code (no body)from Anthropic is the provider shedding load under sustained/long-running generation, not a rejected API key — but the engine routed it onto the credential-rejection lane, ending the run on the first occurrence and discarding partial output. The Builder gateway engine and the AI SDK lane had already been fixed for this exact scenario, but the Anthropic engine (used by the Plan app's generation pipeline) still lacked the fix, so it was systemic to any long-running Anthropic-backed generation, not specific to wireframes.Solution
Reuse the existing shared predicate (
isBareProviderRejectionMessage) — already used by the Builder gateway engine and the AI SDK lane — in the Anthropic engine so all three lanes agree on the same verdict for a reasonless 403. A 403 with no discernible reason is now tagged withPROVIDER_TRANSIENT_REJECTION_ERROR_CODEandproviderRetryable: true, letting the run retry with backoff and surfacing a human-readable, retryable message instead of a bare HTTP status echo. A 403 that carries an actual reason (e.g.permission_error) still keepshttp_403and the credential lane.Key Changes
anthropic-engine.ts: detect a bare/empty-reason 403 viaisBareProviderRejectionMessageand tag it withPROVIDER_TRANSIENT_REJECTION_ERROR_CODE+providerRetryable: trueinstead ofhttp_403, while 403s with a real reason keep existing credential-lane behavior.bare-forbidden-parity.spec.ts: regression coverage asserting that the Anthropic engine, the AI SDK lane (classifyProviderError), and the run-loop's retry/continuation logic all agree that a bare 403 is transient and retryable, that a reasoned 403 stays on the credential lane, and includes a static guard that fails if any future*-engine.tsfile maps`http_${status}`without consulting the shared predicate.anthropic-bare-403-transient.mddocumenting the fix as a patch to@agent-native/core.Notes
To clone this PR locally use the Github CLI with command
gh pr checkout 5041You can tag me at @BuilderIO for anything you want me to fix or change