Repository navigation
SideRepoOps: create_pull_request base-branch resolved via GitHub GET /repos 404, then stringified into the ref name β ERR_SYSTEM merge-base failure (live agent)Β #39404
Description
Activity
- locked and limited conversation to collaborators
on Jun 15, 2026 - unlocked this conversation
on Jun 15, 2026 Fork PR: Tarekchehahde#9
Upstream compare: main...Tarekchehahde:gh-aw:fix/39404-base-branch-404-validation
Approach: Validate branch names (reject 404 JSON bodies), honor sole
allowed-base-branchesentry without API, fail closed with actionable errors, fix misleading "no commits" details on merge-base failures.Local tests:
vitest run git_patch_utils.test.cjs create_pull_request_helpers.test.cjs get_base_branch.test.cjsβ 111/111 pass- added a commit that references this issue
on Jul 11, 2026 - locked and limited conversation to collaborators
on Jul 11, 2026 - unlocked this conversation
on Jul 11, 2026 Hitting the same class of bug on GitHub Enterprise Server (not github.com), with a
401instead of404, so leaving this here rather than filing a duplicate.Setup:
gh awCLI v0.88.7,dnb.ghe.com,create_pull_requestsafe-output targeting a different repo (target-repo:cross-repo, public within the enterprise β not private), Copilot engine.Symptom: intermittent β 2 failures out of 5 consecutive live-mode runs of the same workflow against the same target repo, same checkout config (
fetch-depth: 0, plus a custom step re-fetchingrefs/heads/*after checkout).create_pull_requestfails with:{"result":"error","error":"Pinned SHA <sha> failed to generate patch: ERR_SYSTEM: No remote refs available for merge-base calculation", ...}...even though
git merge-base,git log, and callinggenerateGitPatch()directly all succeed against the exact same checkout moments later in the same job.Root cause found in job logs: the
build_checkout_manifeststep (which runs right after theactions/checkoutsteps, before our custom "fetch additional refs" step populatesrefs/remotes/origin/*) tries to resolve the target repo's default branch two ways, and both fail:[debug] build_checkout_manifest: git default branch lookup failed for <target-repo>: Failed to run git symbolic-ref --short refs/remotes/origin/HEAD fatal: ref refs/remotes/origin/HEAD is not a symbolic ref [debug] build_checkout_manifest: gh api default branch lookup failed for <target-repo>: Failed to run gh api repos/<target-repo> --jq .default_branch gh: Bad credentials (HTTP 401) [info] checkout-manifest: <target-repo> -> path= default_branch=<unresolved> [info] checkout-manifest: <target-repo> -> path=target default_branch=<unresolved>So instead of a stringified 404 body becoming the ref name (as in the original report), here it resolves to the literal string
<unresolved>, which presumably also fails to resolve to a real ref later.Two distinct issues stacked here, same as the original report's diagnosis:
git symbolic-ref refs/remotes/origin/HEADis dead-end βactions/checkout's sparse/manual checkout mode never sets that ref, so this lookup always fails first.- The
gh api repos/<target-repo>fallback doesn't appear to use the same token that successfully performed the checkout of that repo a few steps earlier β it gets401 Bad credentialseven though the checkout itself (usingGH_AW_GITHUB_TOKEN) worked fine for the same repo.
This intermittently poisons later merge-base computation depending on timing between this early manifest-building step and the later "fetch additional refs" step. When it fails 3 retries in the same run, our workflow gracefully falls back to filing an issue with a ready-to-apply patch instead of crashing, so the impact is "no unattended PR" rather than data loss β but it does defeat unattended
create_pull_requestfor cross-repo/live-mode workflows on GHES.Happy to share full anonymized job logs if useful β this reproduced reliably enough (2/5 runs) that I could probably get a fresh repro on request.
- locked and limited conversation to collaborators
on Sep 11, 2026 - unlocked this conversation
on Sep 11, 2026 - added a commit that references this issue
on Sep 30, 2026 Status (2026-10-07; Contributor proposal): A July 11 fork proposal would reject API error bodies used as branch names; a September 11 GHES report describes an intermittent
401variant of the merge-base failure. The next step is to assess that proposal against both404and401cases and verify that failures produce actionable errors rather than invalid refs.
π€ This issue was investigated and filed by Claude Code.
Summary
In a SideRepoOps workflow (an agentic workflow running in repo
<workflow-repo>that targets a private side repository<side-repo>viatarget-repo:), thecreate_pull_requestsafe-output never produces a PR. The MCPcreate_pull_requesthandler fails during patch generation with:The agent had correctly implemented and committed its change locally on a feature branch; the failure is entirely in the handler's patch generation, not the agent's git state.
This is the same
ERR_SYSTEMsymptom as #37836, but observed in a live agent run (not samples mode), and the logs reveal a more specific root cause than the hypothesis in #37836: the base branch is resolved by a GitHubGET /repos/{owner}/{repo}call that returns404 Not Found, and the 404 error object is then stringified and used as the base-branch name.Environment
github/gh-aw-actions/setup@v0.79.1ghcr.io/github/gh-aw-firewall/{agent,api-proxy,squid}:0.25.68ghcr.io/github/gh-aw-mcpg:v0.3.25claude(Claude Code)<workflow-repo>, side repo<side-repo>(both private)Root cause (from the safeoutputs MCP server log)
The handler logs the base branch it will diff against as the JSON body of a 404 response:
So the failing chain is:
GET /repos/{owner}/{repo}API call.404 Not Foundβ the token available to the safeoutputs MCP server cannot see the private side repo (404 = no visibility).baseBranchanddefaultBranch:{"message":"Not Found",...,"status":"404"}.generate_git_patchthen computes a merge-base against the literal reforigin/{"message":"Not Found",...}, which of course doesn't exist locally and can't be fetched, and aborts withERR_SYSTEM: No remote refs available for merge-base calculation.Why #37836's hypothesis does not fully explain this
#37836 hypothesizes that
origin/<baseBranch>simply isn't fetched into the side-repo checkout. In this run that is not the case β the side repo is checked out with full history and all branch refs are fetched:The agent's own diagnostics inside that checkout confirmed
origin/masterresolves and 22refs/remotes/origin/*refs are present locally, andgit merge-base HEAD origin/mastersucceeds. A merge-base againstmasterwould have worked β the handler just never asks formasterbecause the base-branch lookup 404'd and poisoned the ref name.The base branch is also explicitly configured, so the API lookup shouldn't be needed at all:
Likely trigger: missing cross-repo token on
create-pull-requestIn our config,
tools.githubis given a cross-repo token, but thecreate-pull-requestsafe-output has nogithub-token:override. The repro in #37836 passesgithub-token: ${{ secrets.TEMP_USER_PAT }}tocreate-pull-request. It looks like the handler's base-branchGET /reposcall uses a token without read access to the private side repo, producing the 404. (persist-credentials: falsealso means the MCP server has no creds to re-fetch β though that's moot here since the refs are already present locally.)Misleading error surface (matches #37836 secondary observation)
The handler returns the failure as
{"result":"error", ...}inside a JSON-RPC response withisError: false, and thedetailsfield is actively misleading:{"result":"error","error":"Pinned SHA <sha> failed to generate patch: ERR_SYSTEM: No remote refs available for merge-base calculation","details":"No commits were found to create a pull request. Make sure you have committed your changes using git add and git commit before calling create_pull_request."}The
detailstold the agent commits were missing (they weren't). In a live agent run this caused the agent to give up oncreate_pull_requestand fall back to other outputs β every job still reported success, so the run looks green but silently produces no PR. This has recurred on every cross-repo PR attempt in this workflow since ~2026-06-08.Suggested fixes
GET /reposlookup. Never stringify an error object into a branch name.allowed-base-branches(or the checkoutref:) is set, use it for the merge-base instead of an API round-trip β the local checkout already hasorigin/<base>.target-repo:/allowed-repos:) for the base-branch lookup so private side repos return 200, or document thatcreate-pull-request.github-tokenis required for SideRepoOps.result: "error"toisError: true(also raised in samples mode: create_pull_request fails with ERR_SYSTEM merge-base for siderepo workflowsΒ #37836), and correct thedetailstext so it doesn't claim "no commits" when the real failure is base-branch resolution.Redaction note
Private repository names, branch names, commit SHAs, and run URLs have been replaced with placeholders (
<workflow-repo>,<side-repo>,<feature-branch>,<sha>). The literal{"message":"Not Found",...,"status":"404"}strings are reproduced verbatim from the logs because they are the core of the bug. Happy to share additional redacted log context on request.Related: #37836.