libnyanpasu/clash-nyanpasu · error
clash config actor call timed out
Error message
clash config actor call timed out
What it means
This error is raised by `ClashConfigClient::prepare_replace` when the ractor `actor_ref.call(...)` returns `CallResult::Timeout`. `prepare_replace` passes `None` for the timeout, so ractor uses its default (10s) RPC timeout; if the actor does not reply within that window the future resolves to `CallResult::Timeout` and the client bails. The prepare step (serializing state through the legacy bridge) never produced a `PreparedTypedReplace`.
Source
Thrown at backend/tauri/src/client/clash_config.rs:127
.await
}
pub(crate) async fn prepare_replace(
&self,
state: ClashConfig,
) -> anyhow::Result<PreparedTypedReplace<ClashConfig>> {
match self
.inner
.actor_ref
.call(
|reply| ClashConfigActorMessage::PrepareReplace { state, reply },
None,
)
.await?
{
CallResult::Success(result) => result,
CallResult::SenderError => anyhow::bail!("clash config actor reply dropped"),
CallResult::Timeout => anyhow::bail!("clash config actor call timed out"),
}
}
pub(crate) async fn replace_prepared_if_version(
&self,
expected_version: u64,
prepared: PreparedTypedReplace<ClashConfig>,
) -> anyhow::Result<ConditionalReplaceResult<ClashConfigSnapshot>> {
match self
.inner
.actor_ref
.call(
|reply| ClashConfigActorMessage::ReplacePreparedIfVersion {
expected_version,
prepared,
reply,
},
None,View on GitHub (pinned to f7dbce2997)
Solutions
- Check for cross-actor synchronous cycles or long-running handlers in ClashConfigActor that block the mailbox ahead of PrepareReplace.
- Reduce slow work inside handlers (move large disk writes or heavy bridge work off the reply path) so RPCs reply promptly.
- Pass an explicit, generous timeout instead of `None` to `actor_ref.call` in `prepare_replace` if legitimate slow prepares must be tolerated.
- Retry the `replace_if_version` call once the system load or blocking operation has cleared; verify no actor is deadlocked.
Example fix
// before
self.inner.actor_ref.call(
|reply| ClashConfigActorMessage::PrepareReplace { state, reply },
None,
).await?
// after
self.inner.actor_ref.call(
|reply| ClashConfigActorMessage::PrepareReplace { state, reply },
Some(Duration::from_secs(30)),
).await? Defensive patterns
Strategy: retry
Validate before calling
// Pre-check actor responsiveness with a cheap bounded call
match tokio::time::timeout(Duration::from_secs(2), client.get()).await {
Ok(Ok(_)) => { /* actor healthy, proceed with prepare_replace */ }
_ => anyhow::bail!("clash config actor unresponsive; not attempting prepare_replace"),
} Try / catch
match client.replace_if_version(expected_version, next).await {
Err(e) if e.to_string().contains("timed out") => {
// bounded retry with backoff; do not retry unbounded
tokio::time::sleep(Duration::from_secs(1)).await;
client.replace_if_version(expected_version, next).await
}
other => other,
} Prevention
- Keep actor handlers short; move large disk writes off the synchronous reply path.
- Avoid synchronous cross-actor request cycles that can deadlock the mailbox.
- Pass an explicit timeout suited to worst-case persistence time instead of relying on ractor's default.
- Monitor actor mailbox depth / handler durations in tests under load.
When it happens
Trigger: Calling `replace_if_version` → `prepare_replace` while the ClashConfigActor's mailbox is blocked by a long-running earlier message (e.g. a slow `Patch`, `Replace`, or a hung persistence write / legacy bridge call), or the actor is dead in a way that never resolves the call (rare), or the actor is stuck awaiting a locked resource during a slow disk flush of clash-config.yaml.
Common situations: Slow disk or very large clash config making persistence exceed the default RPC timeout; a deadlock between actors (e.g. ClashConfigActor synchronously waiting on another actor that is waiting on it); system under heavy load or suspended; many queued config operations serializing ahead of the prepare request.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- application actor call timed out
- clash config actor reply dropped
- session state actor call timed out
- session state actor reply dropped
- Clash stream actor timed out
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/6821b8568ba98882.
Report an issue: GitHub.