refactor: reuse is_auth_error from provider.rs, DRY backend retry logic

This commit is contained in:
asepharyana
2026-07-18 03:18:27 +07:00
parent 431c8d3b89
commit ff47bffe0c
3 changed files with 4 additions and 13 deletions
@@ -25,19 +25,12 @@ fn step_retry_delay(attempt: u32) -> Duration {
/// Heuristic to decide whether the error is worth retrying.
fn should_retry_subagent_step(err_str: &str) -> bool {
let lower = err_str.to_lowercase();
// Never retry auth/billing failures
if err_str.contains("API error 401")
|| err_str.contains("API error 402")
|| err_str.contains("API error 403")
|| lower.contains("unauthorized")
|| lower.contains("forbidden")
|| lower.contains("authentication failed")
{
if crate::service::provider::is_auth_error(err_str) {
return false;
}
// Never retry abort or user cancellation
if lower.contains("aborted") {
if err_str.to_lowercase().contains("aborted") {
return false;
}
// 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) => {
let err_str = e.to_string();
let is_auth = err_str.contains("API error 401")
|| err_str.contains("API error 402")
|| err_str.contains("API error 403");
let is_auth = crate::service::provider::is_auth_error(&err_str);
// Auth errors are permanent — don't retry.
if is_auth || attempt >= 2 {
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
/// contains them. This is intentionally tighter than `contains("401")`,
/// 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();
// Structured HTTP status patterns
(err_str.contains("API error 401")