gitbutlerapp/gitbutler · error

Failed to fetch review

Error message

Failed to fetch review {review_number}: thread panicked: {e:?}

What it means

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.

Solutions

  1. Read the panic payload in the error message to identify the panicking location and fix or report the underlying bug.
  2. Re-run the command; a transient panic in a dependent crate may not reproduce.
  3. Capture the exact forge/review data that triggered it and file an issue with the payload.
  4. As a workaround, fetch the review via the forge web UI to unblock, then upgrade the library once fixed.
Defensive patterns

Strategy: try-catch

Try / catch

match get_forge_review(db, storage, preferred_user, review_number) {
    Ok(review) => review,
    Err(e) if e.to_string().contains("thread panicked") => {
        log::error!("forge review fetch panicked: {e}");
        // report bug; optionally retry once
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/f8ebf829c9bfaccd. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-forge/src/review.rs:2349

    let storage = storage.clone();

    let review = std::thread::spawn(move || {
        let runtime = tokio::runtime::Runtime::new().map_err(|e| {
            anyhow::anyhow!(
                "Failed fetch review {review_number}: failed to create Tokio runtime: {e}"
            )
        })?;

        runtime.block_on(get_forge_review_inner(
            &preferred_forge_user,
            &forge_repo_info,
            review_number,
            &storage,
        ))
    })
    .join()
    .map_err(|e| {
        anyhow::anyhow!("Failed to fetch review {review_number}: thread panicked: {e:?}")
    })??;

    // Cache the review and ignore any issues, if any.
    crate::db::upsert_review(db, &review).ok();
    Ok(review)
}

/// How to merge a review on the forge. GitHub honours all three;
/// other forges fall back to their default merge strategy when the
/// caller asks for `Squash`/`Rebase`.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "export-schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "lowercase")]
pub enum ReviewMergeMethod {
    #[default]
    Merge,
    Squash,
    Rebase,

View on GitHub (pinned to 58e5313667)