Summary
When a workflow is workflow_dispatch-ed from a ref that is not the default branch, generate_git_patch bases the patch on merge-base(defaultBranch, agent-branch) rather than on the dispatched commit (GITHUB_SHA), which it has already resolved and logged. Two things follow:
- The patch spans the whole dispatched branch, not the agent's work. If the dispatched ref is 13 commits ahead of the default branch, the agent's one-commit fix becomes a 14-commit patch.
- On a partial clone it cannot be generated at all. Diffing from that older base needs base-side blobs the checkout never fetched, so git falls back to a lazy promisor fetch, which is unauthenticated (
persist-credentials: false) and fails — and the failure is reported as Branch 'X' does not exist locally.
Both symptoms are invisible on a default-branch dispatch, because there merge-base == GITHUB_SHA and neither triggers. That is why this can sit unnoticed: it only appears when you dispatch a workflow from a branch to test it.
Trace
[generate_git_patch] Starting patch generation: mode=full, branch=agent-fix/example, defaultBranch=main
[generate_git_patch] Environment: cwd=/home/runner/work/<repo>/<repo>, GITHUB_SHA=<dispatched-sha>
[generate_git_patch] Strategy 1: Using pinned SHA <head-sha> (branch: agent-fix/example)
[generate_git_patch] Strategy 1 (full): Computing merge-base with main (ignoring any stale origin/agent-fix/example)
[generate_git_patch] Strategy 1 (full): origin/main exists locally
[generate_git_patch] Strategy 1 (full): Computed merge-base: <old-sha>
[generate_git_patch] Strategy 1: Resolved baseRef <old-sha> to SHA <old-sha>
[generate_git_patch] Strategy 1: Found 13 commits between <old-sha> and <head-sha>
[generate_git_patch] Strategy 1: Branch 'agent-fix/example' does not exist locally - ERR_SYSTEM: remote: Invalid username or token. Password authentication is not supported for Git operations.
Found 13 commits is the giveaway: the agent made one commit. The other 13 are the dispatched branch's own history. git rev-list --count $(git merge-base origin/main HEAD)..HEAD in that repo returns exactly 13, confirming the base is the merge-base and not GITHUB_SHA.
Why it fails outright on a partial clone
The error handed back to the agent names the object it could not fetch:
Pinned SHA <head-sha> failed to generate patch: ERR_SYSTEM: remote: Invalid username or token.
Password authentication is not supported for Git operations.
fatal: Authentication failed for 'https://github.com/<org>/<repo>.git/'
fatal: could not fetch <sha> from promisor remote
That object is a blob, and it resolves to:
<sha> == git rev-parse <merge-base>:.github/aw/actions-lock.json
i.e. the base-side copy of a file the dispatched branch modified — in this case gh-aw's own actions-lock.json, which any repo upgrading gh-aw on a branch will have changed.
The setup that exposes it:
- The gh-aw checkout is
fetch-depth: 1, so only the checkout commit's blobs are local.
- A workflow step fetches the commit graph cheaply with
git fetch --filter=blob:none --tags --unshallow origin. This is a normal thing to do for git log / git tag --contains access, and it leaves origin marked a promisor remote (remote.origin.promisor=true, remote.origin.partialclonefilter=blob:none) permanently in .git/config.
- Basing the patch on the older merge-base requires that commit's blobs for every changed path. They were never fetched.
- Git lazily fetches them from the promisor remote. gh-aw's checkout is
persist-credentials: false, so that fetch is unauthenticated and fails.
If the base were GITHUB_SHA, step 3 would need no absent blobs at all — the checkout commit's tree is fully local — so the correct base choice also removes the partial-clone failure as a side effect.
The error message is separately wrong
Strategy 1: Branch 'agent-fix/example' does not exist locally - ERR_SYSTEM: remote: Invalid username or token.
The branch did exist locally; the same trace resolved its merge-base and counted its commits two lines earlier. An auth failure while hydrating a blob is reported as a missing local branch, which sends you looking at refs, branch naming, and preserve-branch-name — none of which are involved. This is what makes the underlying problem expensive to find rather than merely annoying.
Expected
- When
GITHUB_SHA is not an ancestor of the default branch (the general case for a non-default-ref workflow_dispatch), base the patch on GITHUB_SHA. gh-aw already resolves and logs it one line above the merge-base computation. This makes the patch contain the agent's commits only, and needs no objects beyond the checkout.
- Determine branch existence from local refs only —
git rev-parse --verify --quiet refs/heads/<branch> — and never report a network/auth error as "does not exist locally".
- Optionally, when a git call fails on a partial clone, say so: "repository is a partial clone (
remote.origin.promisor=true) and the lazy blob fetch is unauthenticated" would be self-diagnosing, since gh-aw knows it emitted persist-credentials: false.
Environment
- gh-aw v0.86.2; also observed on v0.86.1
- engine: copilot, strict security, Linux runner
- checkout: gh-aw defaults (
fetch-depth: 1, persist-credentials: false), plus a workflow step running git fetch --filter=blob:none --tags --unshallow origin
create-pull-request safe output, preserve-branch-name: true, mode=full
- Reproduced on every non-default-branch dispatch (3/3); never on a default-branch dispatch, where the patch generates normally
Note
This supersedes #52153, which I filed with an incorrect root cause (I read it as a v0.85.4 → v0.86.1 regression; the A/B was confounded — the passing arm was a default-branch dispatch, the failing arms were branch dispatches). Closed in favour of this one.
Summary
When a workflow is
workflow_dispatch-ed from a ref that is not the default branch,generate_git_patchbases the patch onmerge-base(defaultBranch, agent-branch)rather than on the dispatched commit (GITHUB_SHA), which it has already resolved and logged. Two things follow:persist-credentials: false) and fails — and the failure is reported asBranch 'X' does not exist locally.Both symptoms are invisible on a default-branch dispatch, because there
merge-base == GITHUB_SHAand neither triggers. That is why this can sit unnoticed: it only appears when you dispatch a workflow from a branch to test it.Trace
Found 13 commitsis the giveaway: the agent made one commit. The other 13 are the dispatched branch's own history.git rev-list --count $(git merge-base origin/main HEAD)..HEADin that repo returns exactly 13, confirming the base is the merge-base and notGITHUB_SHA.Why it fails outright on a partial clone
The error handed back to the agent names the object it could not fetch:
That object is a blob, and it resolves to:
i.e. the base-side copy of a file the dispatched branch modified — in this case gh-aw's own
actions-lock.json, which any repo upgrading gh-aw on a branch will have changed.The setup that exposes it:
fetch-depth: 1, so only the checkout commit's blobs are local.git fetch --filter=blob:none --tags --unshallow origin. This is a normal thing to do forgit log/git tag --containsaccess, and it leaves origin marked a promisor remote (remote.origin.promisor=true,remote.origin.partialclonefilter=blob:none) permanently in.git/config.persist-credentials: false, so that fetch is unauthenticated and fails.If the base were
GITHUB_SHA, step 3 would need no absent blobs at all — the checkout commit's tree is fully local — so the correct base choice also removes the partial-clone failure as a side effect.The error message is separately wrong
The branch did exist locally; the same trace resolved its merge-base and counted its commits two lines earlier. An auth failure while hydrating a blob is reported as a missing local branch, which sends you looking at refs, branch naming, and
preserve-branch-name— none of which are involved. This is what makes the underlying problem expensive to find rather than merely annoying.Expected
GITHUB_SHAis not an ancestor of the default branch (the general case for a non-default-refworkflow_dispatch), base the patch onGITHUB_SHA. gh-aw already resolves and logs it one line above the merge-base computation. This makes the patch contain the agent's commits only, and needs no objects beyond the checkout.git rev-parse --verify --quiet refs/heads/<branch>— and never report a network/auth error as "does not exist locally".remote.origin.promisor=true) and the lazy blob fetch is unauthenticated" would be self-diagnosing, since gh-aw knows it emittedpersist-credentials: false.Environment
fetch-depth: 1,persist-credentials: false), plus a workflow step runninggit fetch --filter=blob:none --tags --unshallow origincreate-pull-requestsafe output,preserve-branch-name: true,mode=fullNote
This supersedes #52153, which I filed with an incorrect root cause (I read it as a v0.85.4 → v0.86.1 regression; the A/B was confounded — the passing arm was a default-branch dispatch, the failing arms were branch dispatches). Closed in favour of this one.