libnyanpasu/clash-nyanpasu · warning

app_handle is not exist

Error message

app_handle is not exist

What it means

Handle::emit sends a serialized event to the Tauri frontend via the stored global AppHandle. If the AppHandle was never set (app not initialized) it bails with 'app_handle is not exist'. This is a lifecycle/ordering error: events are being emitted before the Tauri application handle is registered in the global singleton.

Source

Thrown at backend/tauri/src/core/handle.rs:100

        // Tray::update_systray(app_handle.as_ref().unwrap())?;
        Handle::emit("update_systray", ())?;
        Ok(())
    }

    /// update the system tray state
    pub fn update_systray_part() -> Result<()> {
        let app_handle = Self::global().app_handle.lock();
        if app_handle.is_none() {
            bail!("update_systray unhandled error");
        }
        Tray::update_part(app_handle.as_ref().unwrap())?;
        Ok(())
    }

    pub fn emit<S: Serialize + Clone>(event: &str, payload: S) -> Result<()> {
        let app_handle = Self::global().app_handle.lock();
        if app_handle.is_none() {
            bail!("app_handle is not exist");
        }

        app_handle.as_ref().unwrap().emit(event, payload)?;
        Ok(())
    }
}

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Delay event-producing tasks until after Tauri setup stores the app handle
  2. Make emit fail-soft: log a warning and return Ok when no handle is registered, since UI events are non-critical
  3. Replace Handle::global() with an injected UiEventSink adapter so the dependency is explicit and testable
  4. Use a OnceLock/watch channel so emitters wait for handle availability instead of failing

Example fix

// before
bail!("app_handle is not exist");
// after
let Some(app_handle) = app_handle.as_ref() else {
    log::debug!("emit skipped, app_handle not ready: {event}");
    return Ok(());
};
Defensive patterns

Strategy: try-catch

Validate before calling

fn events_ready(handle: &Handle) -> bool {
    handle.app_handle.lock().is_some()
}

Try / catch

if let Err(e) = Handle::emit("config-changed", payload) {
    log::debug!("event emit skipped: {e}");
}

Prevention

When it happens

Trigger: Calling Handle::emit(event, payload) during early startup before setup() stores the app handle; background tasks (log watchers, core callbacks) firing events at startup or teardown after handle removal.

Common situations: Config/log listeners started before Tauri setup completes; tests exercising event-emitting code without a Tauri runtime; shutdown races where a task emits after the app dropped.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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