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

  1. Simply retry the operation — after the core settles, a fresh refresh() will succeed against the new instance
  2. Serialize core restarts with proxy refreshes so they don't interleave
  3. Check what triggered a core restart during refresh (config change, hotkey, update) and stagger it
  4. If it recurs constantly, look for a loop restarting the core (crash-restart cycle) and fix that root cause
  5. 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

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


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/cd51548da9f5ca77. Report an issue: GitHub.