gitbutlerapp/gitbutler · error
Failed to get PR base repo
Error message
Failed to get PR base repo: {} What it means
get_pull_request_base_repo_url fetches a pull request to determine the git URL of its base repository. A non-success HTTP status from /repos/{owner}/{repo}/pulls/{n} bails with this message and the status. Means the PR could not be read (doesn't exist or inaccessible).
Solutions
- Confirm the PR exists: curl the same /pulls/{n} endpoint with the token
- Check HTTP status in the message: 404 → wrong repo/PR, 403 → permissions or rate limit, 401 → token invalid
- Regenerate or rescope the GitHub token if 401/403
Defensive patterns
Strategy: try-catch
Try / catch
match client.get_pull_request_base_repo_url(owner, repo, pr).await {
Ok(url) => url,
Err(e) if e.to_string().contains("404") => {
eprintln!("PR #{pr} not found in {owner}/{repo}");
Default::default()
}
Err(e) => return Err(e.into()),
} Prevention
- Confirm the PR number belongs to this owner/repo
- Keep tokens fresh and scoped for repo read
- Handle 403 as rate-limit/permission distinctly from 404
When it happens
Trigger: Calling get_pull_request_base_repo_url for a PR number that doesn't exist in owner/repo, a private repo the token can't read, or an API error (403 rate limit, 5xx).
Common situations: Wrong owner/repo pair for the PR number; stale PR reference after deletion; token expired or scope-stripped; rate limiting on shared CI tokens.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Could not update all PR targets before native stack…
- Failed to add labels
- Failed to add to GitHub stack
- Failed to create GitHub stack
- Failed to create pull request
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/c867f806e3f780da.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-github/src/client.rs:437
struct Base {
#[serde(default)]
repo: Option<Repo>,
}
#[derive(Debug, Deserialize)]
struct Repo {
#[serde(default)]
git_url: Option<String>,
#[serde(default)]
clone_url: Option<String>,
}
let url = format!(
"{}/repos/{}/{}/pulls/{}",
self.base_url, owner, repo, pr_number
);
let response = self.client.get(&url).send().await?;
if !response.status().is_success() {
bail!("Failed to get PR base repo: {}", response.status());
}
let body: BaseRepoResponse = response.json().await?;
// git_url matches what TS's parseRemoteUrl sees for the
// project's own base remote.
Ok(body.base.repo.and_then(|r| r.git_url.or(r.clone_url)))
}
/// Fetch just the GitHub-computed merge status for a PR
/// (`mergeable_state` and the comment count). These fields are
/// expensive enough that GitHub recomputes them per call, so we
/// expose them through a focused endpoint that the UI only
/// subscribes to where it actually displays them.
pub async fn get_pull_request_merge_status(
&self,
owner: &str,
repo: &str,
pr_number: i64,
) -> Result<PullRequestMergeStatus> {View on GitHub (pinned to 58e5313667)