gitbutlerapp/gitbutler · critical
panic while executing `{}`: {} panic backtrace unavailable
Error message
panic while executing `{}`: {}
panic backtrace unavailable from hook; conversion backtrace:
{} What it means
The degraded variant of GitButler's panic capture: the panic payload was converted into an error, but no hook snapshot existed, so thread name, panic location, and the hook backtrace are missing; a fresh Backtrace::force_capture() taken at the conversion site is attached instead. It means the same thing (an exported function panicked) with worse diagnostics — typically install_panic_hook() had not run yet, the TLS snapshot was already consumed (nested panic), or the panic happened outside the hooked path.
Source
Thrown at crates/but-api/src/panic_capture.rs:76
.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()
)
}
}
/// Installs a hook that captures panic context and then delegates to the previous hook.
/// Can be called multiple times, but only the first call will install the hook.
pub fn install_panic_hook() {
INSTALL_PANIC_HOOK.call_once(|| {
let previous_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
capture_panic_info(panic_info);
previous_hook(panic_info);
}));
});View on GitHub (pinned to 2497b8007a)
Solutions
- Ensure install_panic_hook() is called before any but-api invocation — it is call_once-safe.
- Use the attached conversion backtrace plus the payload message to locate the panic; enable RUST_BACKTRACE=1 for more frames.
- Fix the underlying panic exactly as with the snapshot variant.
- If nested panics are involved, find the first panic — the second usually hides it.
Defensive patterns
Strategy: try-catch
Try / catch
try {
return await gitbutlerApi.workspaceOp();
} catch (e) {
const msg = String(e?.message ?? e);
if (msg.includes('panic while executing')) {
log.error('rust panic (degraded capture — hook not installed?)', msg);
throw new InternalError('internal error, see logs');
}
throw e;
} Prevention
- Call install_panic_hook() first thing in main() and in test setup.
- Avoid invoking exported APIs before initialization completes — 'backtrace unavailable from hook' wording is an init-order smell.
- Watch for nested panics; the first panic is the real one.
When it happens
Trigger: Panics in but-api functions during early startup or in tests before install_panic_hook() runs; double panics where LAST_PANIC_INFO was already taken; panics on threads where the snapshot failed to record.
Common situations: Test harnesses exercising but-api without the hook; initialization-order bugs where an API call precedes hook installation; panics raised while handling another panic.
Related errors
- panic while executing `{}` on thread '{}' ({}) at {}: {} pa
- failed to create app settings
- askpass broker must be initialized
- InvalidArg
- valid hex prefix
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/695bd0cf0a9c8b5b.
Report an issue: GitHub.