refactor: reuse is_auth_error from provider.rs, DRY backend retry logic
This commit is contained in:
@@ -25,19 +25,12 @@ fn step_retry_delay(attempt: u32) -> Duration {
|
|||||||
|
|
||||||
/// Heuristic to decide whether the error is worth retrying.
|
/// Heuristic to decide whether the error is worth retrying.
|
||||||
fn should_retry_subagent_step(err_str: &str) -> bool {
|
fn should_retry_subagent_step(err_str: &str) -> bool {
|
||||||
let lower = err_str.to_lowercase();
|
|
||||||
// Never retry auth/billing failures
|
// Never retry auth/billing failures
|
||||||
if err_str.contains("API error 401")
|
if crate::service::provider::is_auth_error(err_str) {
|
||||||
|| err_str.contains("API error 402")
|
|
||||||
|| err_str.contains("API error 403")
|
|
||||||
|| lower.contains("unauthorized")
|
|
||||||
|| lower.contains("forbidden")
|
|
||||||
|| lower.contains("authentication failed")
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Never retry abort or user cancellation
|
// Never retry abort or user cancellation
|
||||||
if lower.contains("aborted") {
|
if err_str.to_lowercase().contains("aborted") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Everything else (timeout, 5xx, rate-limit, network blip) is retryable
|
// Everything else (timeout, 5xx, rate-limit, network blip) is retryable
|
||||||
|
|||||||
@@ -421,9 +421,7 @@ fn spawn_single_agent(sp: SpawnCtx<'_>) -> anyhow::Result<String> {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_str = e.to_string();
|
let err_str = e.to_string();
|
||||||
let is_auth = err_str.contains("API error 401")
|
let is_auth = crate::service::provider::is_auth_error(&err_str);
|
||||||
|| err_str.contains("API error 402")
|
|
||||||
|| err_str.contains("API error 403");
|
|
||||||
// Auth errors are permanent — don't retry.
|
// Auth errors are permanent — don't retry.
|
||||||
if is_auth || attempt >= 2 {
|
if is_auth || attempt >= 2 {
|
||||||
let _ = done_tx.send(Err(e));
|
let _ = done_tx.send(Err(e));
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ fn backoff_duration(attempt: u32) -> Duration {
|
|||||||
/// request builders below, plus well-known auth keywords in case the body
|
/// request builders below, plus well-known auth keywords in case the body
|
||||||
/// contains them. This is intentionally tighter than `contains("401")`,
|
/// contains them. This is intentionally tighter than `contains("401")`,
|
||||||
/// which could false-positive on a URL port, model name, or body text.
|
/// which could false-positive on a URL port, model name, or body text.
|
||||||
fn is_auth_error(err_str: &str) -> bool {
|
pub fn is_auth_error(err_str: &str) -> bool {
|
||||||
let err_lower = err_str.to_lowercase();
|
let err_lower = err_str.to_lowercase();
|
||||||
// Structured HTTP status patterns
|
// Structured HTTP status patterns
|
||||||
(err_str.contains("API error 401")
|
(err_str.contains("API error 401")
|
||||||
|
|||||||
Reference in New Issue
Block a user