Watch
1
0
Fork
You've already forked souveraine
0

bifrost: stop sleeping on a spent quota window

retry-after was honoured unclamped, so an 11997s header on 2026-08-16 parked
the subconscious pass for 3h20m with no cancel path. A 429 asking for more
than 60s is a spent window, not a burst, and it does not clear inside the
retry loop — fail it and name the number. claude_subscription answers the
same header by rotating logins; bifrost has nothing to rotate to, so an
absent header keeps the ordinary backoff.
This commit is contained in:
Fimeg 2026-08-17 08:42:19 -04:00
commit 3b11903365

View file

@ -456,6 +456,33 @@ fn retry_cap(body: &str, max_retries: u32) -> u32 {
}
}
/// Longest `retry-after` still worth sleeping on rather than failing the call.
///
/// A window this long is a spent quota, not a burst limit, and it does not
/// clear inside a retry loop. Measured 2026-08-16: an `11997` header put the
/// subconscious pass to sleep for 3h20m with no cancel path and nothing on the
/// wire to say it was waiting rather than dead.
///
/// `claude_subscription.rs` learned this first and answers it by rotating to
/// another login, so there an *absent* `retry-after` also counts as exhausted.
/// Bifrost has nothing to rotate to, so an absent header keeps the ordinary
/// jittered backoff — only an explicitly long one fails fast.
const BURST_RETRY_CEILING_SECS: u64 = 60;
fn is_exhausted_window(status: reqwest::StatusCode, retry_after: Option<Duration>) -> bool {
status.as_u16() == 429
&& retry_after.is_some_and(|d| d.as_secs() > BURST_RETRY_CEILING_SECS)
}
/// Name the window in the error, so a failure says how long the provider
/// wanted rather than only that it refused.
fn reset_hint(retry_after: Option<Duration>) -> String {
match retry_after {
Some(d) => format!(" (provider asked for {}s)", d.as_secs()),
None => String::new(),
}
}
fn classify_status(status: reqwest::StatusCode, body: &str) -> ErrorClass {
match status.as_u16() {
429 => {
@ -731,7 +758,10 @@ impl BifrostClient {
.context("Failed to read Bifrost error body")?;
match classify_status(status, &body_text) {
ErrorClass::Transient if attempt < retry_cap(&body_text, policy.max_retries) => {
ErrorClass::Transient
if attempt < retry_cap(&body_text, policy.max_retries)
&& !is_exhausted_window(status, retry_after) =>
{
let delay = retry_after.unwrap_or_else(|| jittered_delay(attempt, policy));
warn!(
"Bifrost {} on {} (attempt {}), retrying in {:?}",
@ -756,10 +786,11 @@ impl BifrostClient {
body: body_text[..body_text.len().min(300)].to_string(),
});
anyhow::bail!(
"Bifrost returned {} after {} attempt(s) on {}: {}",
"Bifrost returned {} after {} attempt(s) on {}{}: {}",
status,
attempt + 1,
model,
reset_hint(retry_after),
&body_text[..body_text.len().min(500)]
);
}
@ -844,6 +875,53 @@ impl LlmProvider for BifrostClient {
mod tests {
use super::*;
#[test]
fn exhausted_window_rejects_the_measured_11997s_header() {
// The header that put the subconscious to sleep for 3h20m on
// 2026-08-16. It must not be slept on.
let long = Some(Duration::from_secs(11997));
assert!(is_exhausted_window(reqwest::StatusCode::TOO_MANY_REQUESTS, long));
}
#[test]
fn exhausted_window_still_sleeps_on_a_real_burst_limit() {
let short = Some(Duration::from_secs(30));
assert!(!is_exhausted_window(
reqwest::StatusCode::TOO_MANY_REQUESTS,
short
));
// Exactly at the ceiling is still a burst, not a spent window.
let at_ceiling = Some(Duration::from_secs(BURST_RETRY_CEILING_SECS));
assert!(!is_exhausted_window(
reqwest::StatusCode::TOO_MANY_REQUESTS,
at_ceiling
));
}
#[test]
fn exhausted_window_ignores_absent_headers_and_other_statuses() {
// Bifrost has no account to rotate to, so a 429 with no header keeps
// the ordinary jittered backoff — this is where it diverges from
// `claude_subscription::is_exhausted_window` on purpose.
assert!(!is_exhausted_window(
reqwest::StatusCode::TOO_MANY_REQUESTS,
None
));
assert!(!is_exhausted_window(
reqwest::StatusCode::SERVICE_UNAVAILABLE,
Some(Duration::from_secs(11997))
));
}
#[test]
fn reset_hint_names_the_window_or_says_nothing() {
assert_eq!(
reset_hint(Some(Duration::from_secs(11997))),
" (provider asked for 11997s)"
);
assert_eq!(reset_hint(None), "");
}
#[test]
fn test_client_creation() {
let client = BifrostClient::new(