{"record":{"id":"f8ebf829c9bfaccd","repo":"gitbutlerapp/gitbutler","slug":"failed-to-fetch-review-review-number-thread-panicked-e","errorCode":null,"errorMessage":"Failed to fetch review {review_number}: thread panicked: {e:?}","messagePattern":"Failed to fetch review (.+?): thread panicked: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-forge/src/review.rs","lineNumber":2349,"sourceCode":"    let storage = storage.clone();\n\n    let review = std::thread::spawn(move || {\n        let runtime = tokio::runtime::Runtime::new().map_err(|e| {\n            anyhow::anyhow!(\n                \"Failed fetch review {review_number}: failed to create Tokio runtime: {e}\"\n            )\n        })?;\n\n        runtime.block_on(get_forge_review_inner(\n            &preferred_forge_user,\n            &forge_repo_info,\n            review_number,\n            &storage,\n        ))\n    })\n    .join()\n    .map_err(|e| {\n        anyhow::anyhow!(\"Failed to fetch review {review_number}: thread panicked: {e:?}\")\n    })??;\n\n    // Cache the review and ignore any issues, if any.\n    crate::db::upsert_review(db, &review).ok();\n    Ok(review)\n}\n\n/// How to merge a review on the forge. GitHub honours all three;\n/// other forges fall back to their default merge strategy when the\n/// caller asks for `Squash`/`Rebase`.\n#[derive(Debug, Clone, Serialize, Deserialize, Default)]\n#[cfg_attr(feature = \"export-schema\", derive(schemars::JsonSchema))]\n#[serde(rename_all = \"lowercase\")]\npub enum ReviewMergeMethod {\n    #[default]\n    Merge,\n    Squash,\n    Rebase,","sourceCodeStart":2331,"sourceCodeEnd":2367,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-forge/src/review.rs#L2331-L2367","documentation":"After `get_forge_review` spawns a thread that runs the async fetch with `runtime.block_on`, the thread's `join()` result is mapped through this error. `JoinError` means the worker thread panicked (or was cancelled), so the review fetch aborted abnormally. The panic payload is included with `{e:?}` for diagnosis; a panic here usually indicates an unexpected bug inside the forge client rather than a normal API failure.","triggerScenarios":"Any call to `get_forge_review` where the spawned thread panics during `get_forge_review_inner` (e.g. an unwrapped Option/Result, slice index panic, assertion inside the async forge client), or thread cancellation on shutdown.","commonSituations":"Bug in a forge API response handling path that assumes a field exists; running on a platform where a dependency panics on unusual input; process shutdown racing the fetch.","solutions":["Read the panic payload in the error message to identify the panicking location and fix or report the underlying bug.","Re-run the command; a transient panic in a dependent crate may not reproduce.","Capture the exact forge/review data that triggered it and file an issue with the payload.","As a workaround, fetch the review via the forge web UI to unblock, then upgrade the library once fixed."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match get_forge_review(db, storage, preferred_user, review_number) {\n    Ok(review) => review,\n    Err(e) if e.to_string().contains(\"thread panicked\") => {\n        log::error!(\"forge review fetch panicked: {e}\");\n        // report bug; optionally retry once\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Treat this as a library bug: capture the panic payload and file an issue","Keep the library and its tokio/forge dependencies up to date","Avoid running fetches during process shutdown"],"tags":["thread","panic","forge","concurrency"],"backgroundTag":"thread-interrupted","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}