libnyanpasu/clash-nyanpasu · warning
core instance retired during proxy assembly
Error message
core instance retired during proxy assembly
What it means
The proxies subsystem builds a snapshot (proxies + providers) against a mihomo/clash external-controller API instance. Because assembly can take time (multiple HTTP calls), the core instance may have been retired (restarted/replaced) meanwhile; api.is_revoked() detects this and anyhow::ensure! aborts with this error instead of publishing a stale snapshot. It is an intentional staleness guard, not a corruption.
Source
Thrown at backend/tauri/src/core/proxies.rs:119
let result = async {
let (proxies, providers) = api.proxy_snapshot().await?;
let proxies = api::ProxiesRes {
proxies: proxies
.into_iter()
.map(|(name, proxy)| (name.as_str().to_owned(), proxy_item(proxy)))
.collect(),
};
let providers = api::ProvidersProxiesRes {
providers: providers
.into_iter()
.map(|(name, provider)| {
Ok((name.as_str().to_owned(), provider_item(provider)?))
})
.collect::<Result<_>>()?,
};
let proxies = Proxies::from_responses(proxies, providers.clone())?;
let fingerprint = serde_json::to_vec(&(&proxies, &providers))?;
anyhow::ensure!(
!api.is_revoked(),
"core instance retired during proxy assembly"
);
Ok::<_, anyhow::Error>(Arc::new(Snapshot {
api: api.clone(),
proxies,
providers,
fetched: Instant::now(),
fingerprint,
}))
}
.await;
match result {
Ok(snapshot) => {
let changed = self
.cache
.as_ref()
.is_none_or(|old| old.fingerprint != snapshot.fingerprint);View on GitHub (pinned to f7dbce2997)
Solutions
- Simply retry the operation — after the core settles, a fresh refresh() will succeed against the new instance
- Serialize core restarts with proxy refreshes so they don't interleave
- Check what triggered a core restart during refresh (config change, hotkey, update) and stagger it
- If it recurs constantly, look for a loop restarting the core (crash-restart cycle) and fix that root cause
- Treat as transient: UI layers typically show stale data and re-fetch rather than reporting failure
Example fix
// caller-side retry
for _ in 0..3 {
match proxies.refresh().await {
Ok(snap) => break snap,
Err(e) if e.to_string().contains("retired") => continue,
Err(e) => return Err(e),
}
} Defensive patterns
Strategy: retry
Validate before calling
// before an expensive refresh, bail early if the instance is already revoked
if api.is_revoked() {
return Err(anyhow::anyhow!("core instance retired before refresh"));
} Try / catch
match proxies.refresh().await {
Ok(snap) => use(snap),
Err(e) if e.to_string().contains("retired during proxy assembly") => {
tokio::time::sleep(Duration::from_millis(200)).await;
proxies.refresh().await?; // retry against the new core instance
}
Err(e) => return Err(e),
} Prevention
- Avoid triggering core restarts while proxy data is loading
- Serialize config changes and core restarts with proxy refreshes
- Add small backoff + retry for transient staleness errors in UI data loading
- Watch for crash-restart loops that retire instances frequently
When it happens
Trigger: Calling refresh() (directly or through read()/select()) while the core process is being restarted or the API handle is replaced concurrently — the fetched proxy data is then discarded rather than committed.
Common situations: User switches cores or triggers a core restart while the proxies page is loading; hot-reload of config tears down the old controller API mid-refresh; rapid successive proxy selections racing with a core restart.
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 actor timed out; an operation may still be running, do
- promoted target hash mismatch
- cleanup tombstone already exists: {}
- cannot complete materialization with a target hash mismatch
- Failed to send initd signal: {e}
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/cd51548da9f5ca77.
Report an issue: GitHub.