libnyanpasu/clash-nyanpasu · error

proxy actor is unavailable

Error message

proxy actor is unavailable

What it means

Thrown by the proxies service `call` helper when a request to the proxy actor fails for any reason other than success or timeout — typically because the actor is not running, has crashed, or its mailbox/channel is unavailable. Unlike the timeout case, the operation is definitively not in flight, so calls fail fast. Callers of `get`, `providers`, `select`, and `update_provider` all funnel through this error when the actor layer is down.

Solutions

  1. Restart the application (or the actor via the app's restart path) so the proxy actor is respawned and the client gets a live reference.
  2. Ensure `NyanpasuClient`/`AppSupervisor` bootstrap completed before issuing proxy operations; check startup logs for actor spawn failures.
  3. If shutdown races cause it, add graceful shutdown ordering so stop signals wait for in-flight proxy calls to drain.

Example fix

// before: calling proxies API during/after shutdown
let proxies = client.get_proxies().await?; // actor already stopped

// after: ensure bootstrap/actor liveness before use
if client.is_ready().await {
    let proxies = client.get_proxies().await?;
} else {
    anyhow::bail!("proxy actor not started; complete bootstrap first");
}
Defensive patterns

Strategy: fallback

Validate before calling

// Check client/actor readiness before issuing proxy calls
if !client.is_ready().await {
    eprintln!("proxy actor unavailable: complete bootstrap before calling proxies API");
}

Try / catch

match client.get_proxies().await {
    Err(e) if e.to_string().contains("proxy actor is unavailable") => {
        // actor dead — restart app/core; do not spam retries
        eprintln!("restarting core to recover proxy actor");
        client.restart_core().await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any of `get`, `providers`, `select`, or `update_provider` calling `call(...)` when `actor.call(...)` returns an `Err` (send failure / dead actor) or a `CallResult` other than `Success`/`Timeout`, e.g. the actor was never spawned or was stopped during supervision shutdown.

Common situations: The app is shutting down and the actor was stopped mid-request; actor startup failed at bootstrap so the client holds a dead `ActorRef`; a supervision/crash took the actor down; calling the client before `NyanpasuClient` bootstrap finished.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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

Appendix: source

Thrown at backend/tauri/src/core/proxies.rs:331

            snapshots: snapshot_rx,
            changes: changes_rx,
        })))
    }
    async fn call<T: Send + 'static>(
        &self,
        message: impl FnOnce(RpcReplyPort<Result<T>>) -> Message,
    ) -> Result<T> {
        match self
            .0
            .actor
            .call(message, Some(Duration::from_secs(120)))
            .await
        {
            Ok(ractor::rpc::CallResult::Success(result)) => result,
            Ok(ractor::rpc::CallResult::Timeout) => anyhow::bail!(
                "proxy actor timed out; an operation may still be running, do not replay mutations automatically"
            ),
            _ => 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!(

View on GitHub (pinned to f7dbce2997)