- Shipped
- July 22, 2026 at 5:50 PM UTC
- Author
- Kamo
- Commit
- 4f9b3c2
Two bugs in visionComplete, both of which defeated machinery built to handle them. A blank-content response was returned as SUCCESS and ended the retry loop. Every adapter also set finishReason "error" — so the exact failure the loop exists to survive (a model that cannot read the image) stopped it on the FIRST candidate and returned "". The rate-sheet extractor then recorded the page as unreadable without ever trying another model. Blank or finishReason=="error" is now a failed attempt that advances. The catch never recorded auth failures, so a revoked key burned the full attempt budget on every page forever and never tripped the consecutiveAuthFailures>=3 breaker that routing depends on. The streaming and sync paths in this same file both record it; visionComplete did not. Failures are now classified: 401/403 records the auth failure and skips that provider's remaining models, 429/5xx abandons that provider without blaming its key, 400/404/422 advances to the next model, and the success path records success (which resets the breaker). When every candidate fails it THROWS, so the caller gets a 502 that stays distinguishable from the 424 "no provider configured" sentinel — it never returns "". The cap is now at most 4 CALLS rather than 4 candidates, so models skipped for a dead provider do not consume the budget. 9 tests. Run against the previous file, 6 of them fail with exactly the predicted symptoms (empty content where an answer was expected, no throw where one was required, and the two missing recordAuthFailure verifications), so they are not vacuous.