libnyanpasu/clash-nyanpasu · error
failed to download PAC script after {} attempts
Error message
failed to download PAC script after {} attempts What it means
This is the terminal error returned by download_pac_script after all PAC_MAX_RETRIES attempts have failed. It wraps the last underlying error (last_error); if that is somehow None it is produced standalone. It signals that no retry remains and the caller must handle PAC unavailability.
Source
Thrown at backend/tauri/src/core/pac.rs:71
log::warn!(target: "app", "Attempt {}/{} failed: {}", attempt, PAC_MAX_RETRIES, err);
last_error = Some(err);
}
}
Err(e) => {
let err = anyhow::anyhow!("failed to download PAC script: {}", e);
log::warn!(target: "app", "Attempt {}/{} failed: {}", attempt, PAC_MAX_RETRIES, err);
last_error = Some(err);
}
}
// Wait before retrying (except on last attempt)
if attempt < PAC_MAX_RETRIES {
tokio::time::sleep(Duration::from_secs(PAC_RETRY_DELAY)).await;
}
}
Err(last_error.unwrap_or_else(|| {
anyhow::anyhow!(
"failed to download PAC script after {} attempts",
PAC_MAX_RETRIES
)
}))
}
/// Save PAC script to cache directory
pub async fn save_pac_script(script: &str) -> Result<PathBuf> {
let cache_dir = crate::utils::dirs::cache_dir()?;
let pac_file = cache_dir.join("pac.js");
fs::write(&pac_file, script)
.await
.context("failed to save PAC script")?;
Ok(pac_file)
}
View on GitHub (pinned to f7dbce2997)
Solutions
- Inspect the wrapped cause (last_error) printed in earlier 'Attempt N/M failed' warnings to find the root cause
- Fix the underlying network/URL problem identified in the wrapped error
- Ensure the app's fallback path (fallback_to_direct_proxy) runs so the system proxy still works
- Increase PAC_MAX_RETRIES or PAC_RETRY_DELAY for unreliable networks
- Cache a previously downloaded PAC script locally and use it when download fails
Example fix
// caller-side handling
match Pac::download_pac_script().await {
Ok(script) => { /* install script */ }
Err(e) => {
log::warn!("PAC unavailable: {e}; falling back to direct proxy");
Pac::fallback_to_direct_proxy()?;
}
} Defensive patterns
Strategy: fallback
Try / catch
match download_pac_script().await {
Ok(script) => install(script),
Err(e) => {
log::warn!("PAC unavailable after retries: {e}; falling back to direct proxy");
fallback_to_direct_proxy().unwrap_or_else(|fe| log::error!("no system proxy either: {fe}"));
}
} Prevention
- Always implement a fallback proxy mode when PAC download fails
- Cache the last successful PAC script and use it offline
- Increase retry count/delay for mobile or flaky networks
- Alert on repeated download failures so the URL gets fixed
When it happens
Trigger: Calling download_pac_script when every attempt fails with transport errors, non-2xx statuses, or body-read failures — after PAC_MAX_RETRIES attempts separated by PAC_RETRY_DELAY seconds.
Common situations: Prolonged server outage or network disconnection during startup; permanently wrong PAC URL; server blocking the client; offline laptop starting the app with no connectivity.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- failed to read PAC script content: {}
- failed to download PAC script, status: {}
- failed to download PAC script: {}
- proxy selection succeeded; connection interruption error: {:
- proxy actor timed out; an operation may still be running, do
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/eca8044ef7838813.
Report an issue: GitHub.