libnyanpasu/clash-nyanpasu · critical

NyanpasuClient is not available for verge patch

Error message

NyanpasuClient is not available for verge patch

What it means

patch_verge_entrypoint is a migration bridge: it tries to fetch the managed NyanpasuClient from the Tauri app state and delegate the verge config patch to it. If the condition that guards the try_state lookup fails (client not yet registered in app state), it bails with "NyanpasuClient is not available for verge patch" — meaning dependency injection of the app facade hasn't happened.

Source

Thrown at backend/tauri/src/feat.rs:130

/// typed actors are reseeded after a legacy side-effect write. Falls back to a direct
/// `patch_verge` with the managed `NyanpasuClient` during early startup when the bridge
/// is not yet managed.
async fn patch_verge_entrypoint(patch: IVerge) -> Result<()> {
    // TODO(actor-migration): compatibility bridge for legacy feature toggles.
    // Reason: feature paths still enter through Handle::global() and legacy Config::verge().
    // Remove when: feature toggles call typed actor clients through injected command adapters.
    let app_handle = handle::Handle::global().app_handle.lock().clone();
    if let Some(app_handle) = app_handle {
        if let Some(legacy) = app_handle.try_state::<crate::bridge::verge::LegacyVergeBridge>() {
            let legacy = legacy.inner().clone();
            legacy.patch_verge_config(patch).await?;
            return Ok(());
        }
        if let Some(client) = app_handle.try_state::<crate::client::NyanpasuClient>() {
            return patch_verge(client.inner().clone(), patch).await;
        }
    }
    bail!("NyanpasuClient is not available for verge patch")
}

// 切换系统代理
pub fn toggle_system_proxy() {
    let enable = Config::verge().draft().enable_system_proxy;
    let enable = enable.unwrap_or(false);

    tauri::async_runtime::spawn(async move {
        match patch_verge_entrypoint(IVerge {
            enable_system_proxy: Some(!enable),
            ..IVerge::default()
        })
        .await
        {
            Ok(_) => handle::Handle::refresh_verge(),
            Err(err) => log::error!(target: "app", "{err:?}"),
        }
    });

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Ensure NyanpasuClient is managed via app.manage(NyanpasuClient) in the bootstrap/setup before any UI, tray, or hotkey handlers can run.
  2. Delay tray/hotkey registration until after the client is injected into state.
  3. In tests, construct and manage a NyanpasuClient (or call patch_verge directly with a client instance) instead of relying on app_handle state.

Example fix

// before
if let Some(client) = app_handle.try_state::<crate::client::NyanpasuClient>() {
    return patch_verge(client.inner().clone(), patch).await;
}
bail!("NyanpasuClient is not available for verge patch")
// after
// in setup, before registering tray/hotkeys:
let client = NyanpasuClient::new(deps).await?;
app.manage(client);
// then patch_verge_entrypoint will find the client
Defensive patterns

Strategy: try-catch

Validate before calling

// before triggering any verge toggle, confirm the client is managed:
let ready = app_handle.try_state::<crate::client::NyanpasuClient>().is_some();
if !ready { eprintln!("app bootstrap not finished yet"); return; }

Try / catch

match patch_verge_entrypoint(app_handle, patch).await {
    Err(e) if e.to_string().contains("NyanpasuClient is not available") => {
        // retry after bootstrap or queue the patch until setup completes
    },
    other => other?,
}

Prevention

When it happens

Trigger: Calling toggle_system_proxy / enable_system_proxy / disable_system_proxy / toggle_tun_mode / enable_tun_mode / disable_tun_mode before NyanpasuClient is inserted into Tauri managed state (e.g. during early startup, before bootstrap completes, or after a failed bootstrap).

Common situations: Hotkey/tray actions firing during app initialization before setup() manages the client; a panic or error earlier in bootstrap that skipped state management; calling these feat functions from tests without a Tauri app handle with managed state.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


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