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
- Read the embedded location and message — they name the exact file:line that panicked; that, not this wrapper, is the bug.
- Reproduce with matching repository state; the snapshot backtrace is already attached, RUST_BACKTRACE=1 adds frames.
- Search the repo for that line and fix the unwrap/expect or validate the input.
- 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
- Prefer ?, ok_or, and checked access over unwrap/expect in code behind but-api.
- Keep install_panic_hook() as early as possible in startup so snapshots are always available.
- Wrap napi entry points so panics surface as structured errors, never as aborted processes.
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
- panic while executing `{}`: {} panic backtrace unavailable
- InvalidArg
- valid hex prefix
- object for prefix exists
- 'dot' (graphviz) must be installed on the system
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/2a3852655018fafc.
Report an issue: GitHub.