libnyanpasu/clash-nyanpasu · warning
provider snapshot belongs to a retired instance
Error message
provider snapshot belongs to a retired instance
What it means
ProxiesClient::providers fetches a provider snapshot from the proxies actor and validates that the snapshot's api handle is not revoked. If the underlying core instance was replaced/restarted after the snapshot was taken, the snapshot belongs to a retired instance and is rejected, preventing stale provider data from being served.
Source
Thrown at backend/tauri/src/core/proxies.rs:349
_ => anyhow::bail!("proxy actor is unavailable"),
}
}
pub async fn get(&self, force: bool) -> Result<Proxies> {
let snapshot = self.call(|reply| Message::Read { force, reply }).await?;
anyhow::ensure!(
!snapshot.api.is_revoked(),
"proxy snapshot belongs to a retired instance"
);
Ok(snapshot.proxies.clone())
}
pub async fn providers(&self) -> Result<api::ProvidersProxiesRes> {
let snapshot = self
.call(|reply| Message::Read {
force: false,
reply,
})
.await?;
anyhow::ensure!(
!snapshot.api.is_revoked(),
"provider snapshot belongs to a retired instance"
);
Ok(snapshot.providers.clone())
}
pub async fn select(&self, group: String, name: String, interrupt: bool) -> Result<()> {
self.call(|reply| Message::Select {
group,
name,
interrupt,
reply,
})
.await
}
pub async fn update_provider(&self, name: String) -> Result<()> {
self.call(|reply| Message::UpdateProvider { name, reply })
.await
}View on GitHub (pinned to f7dbce2997)
Solutions
- Retry after the core becomes ready so the actor obtains a fresh snapshot
- Trigger a proxy actor refresh/rebind to the live API before querying providers
- Treat as transient in callers and surface a retry affordance to the user
Example fix
// before let providers = proxies_client.providers().await?; // after let providers = retry_on_retired(|| proxies_client.providers(), 3, Duration::from_millis(500)).await?;
Defensive patterns
Strategy: retry
Validate before calling
// ensure core is ready before reading providers
if core_client.status().await? != CoreStatus::Running { core_client.wait_ready(Duration::from_secs(5)).await?; }
let providers = proxies_client.providers().await?; Try / catch
match proxies_client.providers().await {
Ok(p) => p,
Err(e) if e.to_string().contains("retired instance") => /* refresh/retry after core ready */,
Err(e) => return Err(e),
} Prevention
- Gate provider reads on core readiness signals
- Retry transient snapshot staleness errors with a small backoff
- Invalidate cached provider views when the core instance changes
When it happens
Trigger: Calling `proxies_client.providers()` after a core restart/replace or while the external controller handle in the cached snapshot reports is_revoked() == true.
Common situations: Provider list refresh in the UI racing a core update or restart; switching core types; core process crash with actor holding the old snapshot.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- proxy snapshot belongs to a retired instance
- application actor call timed out
- clash config actor reply dropped
- clash config actor call timed out
- session state actor reply dropped
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/cd2f5e8e7a8d6f5d.
Report an issue: GitHub.