{"record":{"id":"578e2c7b91fa0090","repo":"libnyanpasu/clash-nyanpasu","slug":"nyanpasuclient-is-not-available-for-verge-patch","errorCode":null,"errorMessage":"NyanpasuClient is not available for verge patch","messagePattern":"NyanpasuClient is not available for verge patch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"backend/tauri/src/feat.rs","lineNumber":130,"sourceCode":"/// typed actors are reseeded after a legacy side-effect write. Falls back to a direct\n/// `patch_verge` with the managed `NyanpasuClient` during early startup when the bridge\n/// is not yet managed.\nasync fn patch_verge_entrypoint(patch: IVerge) -> Result<()> {\n    // TODO(actor-migration): compatibility bridge for legacy feature toggles.\n    // Reason: feature paths still enter through Handle::global() and legacy Config::verge().\n    // Remove when: feature toggles call typed actor clients through injected command adapters.\n    let app_handle = handle::Handle::global().app_handle.lock().clone();\n    if let Some(app_handle) = app_handle {\n        if let Some(legacy) = app_handle.try_state::<crate::bridge::verge::LegacyVergeBridge>() {\n            let legacy = legacy.inner().clone();\n            legacy.patch_verge_config(patch).await?;\n            return Ok(());\n        }\n        if let Some(client) = app_handle.try_state::<crate::client::NyanpasuClient>() {\n            return patch_verge(client.inner().clone(), patch).await;\n        }\n    }\n    bail!(\"NyanpasuClient is not available for verge patch\")\n}\n\n// 切换系统代理\npub fn toggle_system_proxy() {\n    let enable = Config::verge().draft().enable_system_proxy;\n    let enable = enable.unwrap_or(false);\n\n    tauri::async_runtime::spawn(async move {\n        match patch_verge_entrypoint(IVerge {\n            enable_system_proxy: Some(!enable),\n            ..IVerge::default()\n        })\n        .await\n        {\n            Ok(_) => handle::Handle::refresh_verge(),\n            Err(err) => log::error!(target: \"app\", \"{err:?}\"),\n        }\n    });","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/feat.rs#L112-L148","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure NyanpasuClient is managed via app.manage(NyanpasuClient) in the bootstrap/setup before any UI, tray, or hotkey handlers can run.","Delay tray/hotkey registration until after the client is injected into state.","In tests, construct and manage a NyanpasuClient (or call patch_verge directly with a client instance) instead of relying on app_handle state."],"exampleFix":"// before\nif let Some(client) = app_handle.try_state::<crate::client::NyanpasuClient>() {\n    return patch_verge(client.inner().clone(), patch).await;\n}\nbail!(\"NyanpasuClient is not available for verge patch\")\n// after\n// in setup, before registering tray/hotkeys:\nlet client = NyanpasuClient::new(deps).await?;\napp.manage(client);\n// then patch_verge_entrypoint will find the client","handlingStrategy":"try-catch","validationCode":"// before triggering any verge toggle, confirm the client is managed:\nlet ready = app_handle.try_state::<crate::client::NyanpasuClient>().is_some();\nif !ready { eprintln!(\"app bootstrap not finished yet\"); return; }","typeGuard":null,"tryCatchPattern":"match patch_verge_entrypoint(app_handle, patch).await {\n    Err(e) if e.to_string().contains(\"NyanpasuClient is not available\") => {\n        // retry after bootstrap or queue the patch until setup completes\n    },\n    other => other?,\n}","preventionTips":["Manage NyanpasuClient in tauri setup() before registering tray, hotkeys, or IPC commands.","Disable tray/hotkey actions until a \"bootstrap ready\" flag is set.","In tests, inject a real NyanpasuClient rather than relying on app state."],"tags":["dependency-injection","tauri","initialization-order"],"backgroundTag":"missing-required-config","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}