gitbutlerapp/gitbutler · critical

panic while executing `{}` on thread '{}' ({}) at {}: {} pa

Error message

panic while executing `{}` on thread '{}' ({}) at {}: {}

panic backtrace:
{}

What it means

This is GitButler's panic capture: an exported #[but_api] function panicked, the installed panic hook stored a snapshot (thread name and id, panic file:line, payload message, backtrace) in thread-local storage, and the wrapper converted it into a regular anyhow error so the panic never crosses the napi/FFI boundary. The text embeds everything needed to diagnose the real bug. Treat it as an internal bug report, not an actionable failure of your call.

Source

Thrown at crates/but-api/src/panic_capture.rs:63

/// back to payload-derived message plus a backtrace captured during conversion.
///
/// Assumes that the panic hook has been installed via [`install_panic_hook()`], or
/// else the panic message will not have a backtrace.
pub(crate) fn panic_payload_to_anyhow(
    function_name: &'static str,
    panic_payload: Box<dyn Any + Send + 'static>,
) -> anyhow::Error {
    let payload_message = panic_payload_message(panic_payload.as_ref());
    let snapshot = LAST_PANIC_INFO.with(|slot| slot.borrow_mut().take());

    if let Some(snapshot) = snapshot {
        let panic_message = snapshot
            .payload_message
            .as_deref()
            .or(payload_message.as_deref())
            .unwrap_or("panic payload was not a string");
        let panic_location = snapshot.location.as_deref().unwrap_or("<unknown location>");
        anyhow::anyhow!(
            "panic while executing `{}` on thread '{}' ({}) at {}: {}\n\npanic backtrace:\n{}",
            function_name,
            snapshot.thread_name,
            snapshot.thread_id,
            panic_location,
            panic_message,
            snapshot.backtrace
        )
    } else {
        let panic_message = payload_message
            .as_deref()
            .unwrap_or("panic payload was not a string");
        anyhow::anyhow!(
            "panic while executing `{}`: {}\n\npanic backtrace unavailable from hook; conversion backtrace:\n{}",
            function_name,
            panic_message,
            std::backtrace::Backtrace::force_capture()
        )

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Read the embedded location and message — they name the exact file:line that panicked; that, not this wrapper, is the bug.
  2. Reproduce with matching repository state; the snapshot backtrace is already attached, RUST_BACKTRACE=1 adds frames.
  3. Search the repo for that line and fix the unwrap/expect or validate the input.
  4. If it is a genuine GitButler bug, file an issue with the full error string including the backtrace.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await gitbutlerApi.workspaceOp();
} catch (e) {
  const msg = String(e?.message ?? e);
  if (msg.startsWith('panic while executing')) {
    log.error('rust panic in but-api', msg); // contains location + backtrace
    throw new InternalError('internal error, see logs');
  }
  throw e;
}

Prevention

When it happens

Trigger: Any Rust panic (unwrap on None, index out of bounds, assertion) inside a but-api exported function while the panic hook is installed and its LAST_PANIC_INFO snapshot is available — e.g. an unexpected None from a metadata lookup or an index overrun while iterating a workspace.

Common situations: Repository states the code did not anticipate (empty workspaces, missing refs); races between concurrent UI calls; regressions in but_core/but_api shipped to the desktop app.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/2a3852655018fafc. Report an issue: GitHub.