gitbutlerapp/gitbutler · critical

tauri event emission doesn't fail in practice

Error message

tauri event emission doesn't fail in practice

What it means

Tauri's `AppHandle::emit` broadcasts an event (here "git_prompt", carrying a git credential prompt collected by but_askpass) to all webview listeners and returns a `Result`. It fails when the payload cannot be serialized, when the internal event manager or channel is shut down (typically during app teardown), or when no live listener target exists. The `.expect` converts that rare failure into a panic inside the askpass callback, which takes down the whole desktop app the next time git asks for credentials.

Source

Thrown at crates/gitbutler-tauri/src/main.rs:151

                inherit_interactive_login_shell_environment_if_not_launched_from_terminal();
                migrate_projects().ok();

                tracing::info!(
                    "system git executable for fetch/push: {git:?}",
                    git = gix::path::env::exe_invocation(),
                );
                if cfg!(windows) {
                    tracing::info!("system git bash: {bash:?}", bash = gix::path::env::shell());
                } else {
                    tracing::info!("SHELL env: {var:?}", var = std::env::var_os("SHELL"));
                }

                but_askpass::init({
                    let handle = app_handle.clone();
                    move |event| {
                        handle
                            .emit("git_prompt", event)
                            .expect("tauri event emission doesn't fail in practice")
                    }
                });


                tracing::info!(version = %app_handle.package_info().version,
                                   name = %app_handle.package_info().name, "starting app");

                app_handle.manage(WindowState::new(app_handle.clone()));

                app_settings.watch_in_background({
                    let app_handle = app_handle.clone();
                    move |app_settings| {
                        gitbutler_tauri::ChangeForFrontend::from(app_settings).send(&app_handle)
                    }
                })?;

                let archival = but_feedback::Archival {
                    cache_dir: app_cache_dir.clone(),

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Replace the .expect with a logged non-fatal branch (tracing::warn) so an emit failure can never crash the app
  2. If the frontend must answer the prompt, make the askpass request fail so git aborts instead of hanging without an answer
  3. Guard the shutdown race: skip emitting or ignore emit errors once the app is exiting
  4. Add a test asserting the git_prompt payload is Serialize and matches the frontend's expected shape

Example fix

// before
but_askpass::init({
    let handle = app_handle.clone();
    move |event| {
        handle
            .emit("git_prompt", event)
            .expect("tauri event emission doesn't fail in practice")
    }
});

// after
but_askpass::init({
    let handle = app_handle.clone();
    move |event| {
        if let Err(err) = handle.emit("git_prompt", event) {
            tracing::warn!("failed to emit git_prompt event: {err}");
        }
    }
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: confirm a listener target still exists before emitting
if handle.webview_windows().is_empty() {
    tracing::debug!("no windows; skipping git_prompt emit");
    return;
}

Try / catch

move |event| {
    if let Err(err) = handle.emit("git_prompt", event) {
        tracing::warn!("git_prompt emit failed: {err}");
        // fail the askpass request so git does not block awaiting an answer
    }
}

Prevention

When it happens

Trigger: A git operation over HTTPS without a cached credential fires the but_askpass::init callback at crates/gitbutler-tauri/src/main.rs:151, and `handle.emit("git_prompt", event)` returns Err — e.g. the payload type no longer implements Serialize after a refactor, the event manager is shut down while the app is quitting, or the webview was already destroyed.

Common situations: App shutdown racing an in-flight credential prompt; a Tauri major-version upgrade changing emit or event-manager semantics; a payload struct edited without keeping Serialize; emitting after the last window closed.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/a913effc12bebdd3. Report an issue: GitHub.