GitHub PR creation targets upstream instead of gh's default repo when the checkout has a non-GitHub remote#24383

opened6d agobyJJoshuaRileyDev

What happened

Every time I try to press the button to work with Git, like creating a pull request, I get the following error: Source control provider github failed in createChangeRequest: GitHub returned an error: Validation Failed; base invalid; head invalid

It only happens in one project, a fork, and in every thread of that project. Commit and push work. Only the PR step fails. The user wants PRs to go to their fork (origin), against a branch on that fork, which they set with gh repo set-default.

Diagnosis

The PR is sent to the wrong repository: upstream instead of the gh repo set-default repo (origin). The cause is a non-GitHub remote in the checkout.

The checkout has three fetch remotes:

1container-use <home>/.config/container-use/repos/github.com/<user>/<repo> (local path, added by the container-use tool)
2origin https://github.com/<user>/<repo>.git (the fork)
3upstream https://github.com/<org>/<repo>.git

plus remote.origin.gh-resolved = base (from gh repo set-default <user>/<repo>).

GitHubSourceControlProvider.createChangeRequest calls resolveRepository → resolveGitHubRepository → selectGitHubBaseRepository (packages/source-control-github/src/server/gitHubRepositoryResolution.ts). That loop returns null for the whole selection as soon as any remote fails to parse as a repo on the API host:

ts
1for (const remote of parseFetchRemotes(input.remotes)) {
2 const [remoteHost, owner, name, ...rest] = normalizeGitRemoteUrl(remote.url).split("/");
3 if (remoteHost !== host || !owner || !name || rest.length > 0) return null;
4 repositories.set(remote.name, { owner, name });
5}

A local-path remote normalizes to /users/.../github.com/<user>/<repo>, so remoteHost is "". The gh-resolved mark is never read. resolveGitHubRepository then falls back to its own ranking (upstream, github, origin) and picks upstream. T3 then POSTs repos/<org>/<repo>/pulls with base: fork and a bare head: <branch>. Neither branch exists in <org>/<repo>, so GitHub returns 422 base invalid; head invalid.

Plain gh pr create in the same worktree goes to the right repo, because gh skips remotes that aren't on a GitHub host. A remote on another host (GitLab mirror, local bare repo, etc.) would probably hit the same return null.

Suggested fix: in selectGitHubBaseRepository, continue past remotes that aren't on host (or that don't parse) instead of returning null, so gh-resolved and the ranking only consider GitHub remotes. This logic is unchanged on main as of 2026-10-24 (a195ba080).

Steps to reproduce

  1. Clone a fork: origin = github.com/<you>/<repo>, upstream = github.com/<org>/<repo>.
  2. gh repo set-default <you>/<repo>, which writes remote.origin.gh-resolved=base.
  3. Create a branch fork and push it to origin only. Set it as the PR base (for example branch.<b>.gh-merge-base fork).
  4. Add a remote that points at a local path: git remote add local-mirror /tmp/some-bare-repo.git.
  5. In T3 Code, open a thread on a worktree branch and run commit, push and create PR.
  6. Result: Validation Failed; base invalid; head invalid, because the PR was POSTed to <org>/<repo>.
  7. git remote remove local-mirror and retry: the PR goes to <you>/<repo> as expected.

Version

0.1.1-nightly.20261017.3238 (T3 Code Nightly desktop app). Code path unchanged on main / 0.1.3-nightly.20261024.3604.

Environment

macOS 26.2 (Darwin 25.2.0, arm64, Mac mini), Node v26.12.1, gh 2.86.0. Desktop app with its local server (desktop-managed), used on the same machine. Triage CLI 0.1.2.

Evidence

shell
1# server.trace.ndjson: span runPrStep (after successful runCommitStep + pushCurrentBranch.pushWithUpstream)
2SourceControlProviderError: Source control provider github failed in createChangeRequest: GitHub returned an error: Validation Failed; base invalid; head invalid
3 at .../app.asar/apps/server/dist/binCli-DslDRLSw.mjs:149101:64
4 at runPrStep (Nightly)
5 at runStackedAction.runAction (.../binCli-DslDRLSw.mjs:270520:5)
6 at runStackedAction (.../binCli-DslDRLSw.mjs:270678:125)
7 at ws.rpc.git.runStackedAction (Nightly) {
8 [cause]: GitHubApiResponseError: GitHub returned an error: Validation Failed; base invalid; head invalid
9 at .../binCli-DslDRLSw.mjs:138302:34
10}
11
12# git state in the worktree
13$ git config --get-regexp 'remote\..*\.gh-resolved'
14remote.origin.gh-resolved base
15$ gh repo set-default --view
16<user>/<repo>
17$ git ls-remote --heads origin # has: canary, fork, <branch>
18$ git ls-remote --heads upstream # has: canary (no fork, no <branch>)

Related issues

#7382: fork PR head selector is computed against origin rather than gh's resolved base. Same symptom family, but that issue has upstream as the base and an unqualified head. Here the base repo itself is chosen wrongly because a non-GitHub remote disables gh-resolved. #15023: project identity ignores gh repo set-default. Related, but about project identity, not PR creation.

Fix applied or workaround

Nothing was changed on the machine. Workarounds: remove the non-GitHub remote (git remote remove container-use; container-use may add it back), or create the PR by hand with gh pr create --repo <user>/<repo> --base fork.

Filed by

Claude Code (claude-opus-5-5) via t3 triage