{"record":{"id":"2a3852655018fafc","repo":"gitbutlerapp/gitbutler","slug":"panic-while-executing-on-thread-at","errorCode":null,"errorMessage":"panic while executing `{}` on thread '{}' ({}) at {}: {}\n\npanic backtrace:\n{}","messagePattern":"panic while executing `(.+?)` on thread '(.+?)' \\((.+?)\\) at (.+?): (.+?)\n\npanic backtrace:\n(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/but-api/src/panic_capture.rs","lineNumber":63,"sourceCode":"/// back to payload-derived message plus a backtrace captured during conversion.\n///\n/// Assumes that the panic hook has been installed via [`install_panic_hook()`], or\n/// else the panic message will not have a backtrace.\npub(crate) fn panic_payload_to_anyhow(\n    function_name: &'static str,\n    panic_payload: Box<dyn Any + Send + 'static>,\n) -> anyhow::Error {\n    let payload_message = panic_payload_message(panic_payload.as_ref());\n    let snapshot = LAST_PANIC_INFO.with(|slot| slot.borrow_mut().take());\n\n    if let Some(snapshot) = snapshot {\n        let panic_message = snapshot\n            .payload_message\n            .as_deref()\n            .or(payload_message.as_deref())\n            .unwrap_or(\"panic payload was not a string\");\n        let panic_location = snapshot.location.as_deref().unwrap_or(\"<unknown location>\");\n        anyhow::anyhow!(\n            \"panic while executing `{}` on thread '{}' ({}) at {}: {}\\n\\npanic backtrace:\\n{}\",\n            function_name,\n            snapshot.thread_name,\n            snapshot.thread_id,\n            panic_location,\n            panic_message,\n            snapshot.backtrace\n        )\n    } else {\n        let panic_message = payload_message\n            .as_deref()\n            .unwrap_or(\"panic payload was not a string\");\n        anyhow::anyhow!(\n            \"panic while executing `{}`: {}\\n\\npanic backtrace unavailable from hook; conversion backtrace:\\n{}\",\n            function_name,\n            panic_message,\n            std::backtrace::Backtrace::force_capture()\n        )","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-api/src/panic_capture.rs#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await gitbutlerApi.workspaceOp();\n} catch (e) {\n  const msg = String(e?.message ?? e);\n  if (msg.startsWith('panic while executing')) {\n    log.error('rust panic in but-api', msg); // contains location + backtrace\n    throw new InternalError('internal error, see logs');\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["panic","ffi","napi","backtrace","internal-error"],"backgroundTag":"rust-panic-across-ffi","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}