gitbutlerapp/gitbutler · error · anyhow::Error
Failed to join thread: {e:?}
Error message
Failed to join thread: {e:?} What it means
To fetch GitHub CI checks synchronously, but-forge spawns a thread that builds a tokio runtime and calls but_github::checks::list_for_ref. join() returned Err, meaning that thread panicked; the error embeds the panic payload's Debug output. Note the thread body uses Runtime::new().unwrap(), so tokio runtime creation failure is one concrete panic source inside that thread.
Source
Thrown at crates/but-forge/src/ci.rs:119
let owner = owner.clone();
let repo = repo.clone();
let storage = storage.clone();
let reference = reference.to_string();
let reference_for_checks = reference.clone();
let checks = std::thread::spawn(move || {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(but_github::checks::list_for_ref(
preferred_account.as_ref(),
&owner,
&repo,
&reference,
&storage,
))
})
.join()
.map_err(|e| anyhow::anyhow!("Failed to join thread: {e:?}"))?;
checks.map(|maybe_runs| {
maybe_runs.map(|runs| {
runs.into_iter()
.map(|check| {
let mut ci_check = CiCheck::from(check);
ci_check.reference = reference_for_checks.to_string();
ci_check
})
.collect()
})
})
}
ForgeName::GitLab => {
let preferred_account = preferred_forge_user
.as_ref()
.and_then(|user| user.gitlab().cloned());
let gl = but_gitlab::GitLabClient::from_storage(storage, preferred_account.as_ref())?;
View on GitHub (pinned to caf1f223d3)
Solutions
- Inspect the {e:?} payload in the message - it names the panic reason
- If it mentions runtime or io creation errors, raise ulimits (threads, fds) for the application process
- Otherwise capture the panic output and report it upstream with the forge, owner/repo and ref
Defensive patterns
Strategy: fallback
Try / catch
match list_ci_checks(&forge, owner, repo, reference) {
Err(err) if err.to_string().contains("Failed to join thread") => {
// render CI status as 'unavailable' and keep the rest of the PR view working
}
result => result,
} Prevention
- Keep process ulimits (threads, fds) generous in packaged desktop apps
- Throttle concurrent CI lookups instead of firing one thread per PR
- Log the panic payload verbatim - it is the only clue to the in-thread panic
When it happens
Trigger: Any panic inside the GitHub checks thread: Runtime::new().unwrap() failing under resource pressure (thread/fd limits), or a bug in the checks listing or CiCheck mapping code.
Common situations: Sandboxed desktop environments or containers with low thread/file-descriptor ulimits; regressions in the GitHub checks response mapping.
Related errors
- Failed to create tokio runtime: {err}
- Could not restore all review targets after the push failed:
- Could not restore native GitHub stack membership after the p
- GitHub GraphQL enablePullRequestAutoMerge returned an empty
- GitHub GraphQL disablePullRequestAutoMerge returned an empty
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/40ce79f3132fb76f.
Report an issue: GitHub.