You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
safe-outputs.create-pull-request.preserve-branch-name: true is silently bypassed when the agent-supplied branch name already exists on the remote. The collision handler unconditionally appends a random 8-char hex salt (-[0-9a-f]{8}) to the branch name, defeating the feature's stated purpose.
This is particularly painful for workflows that use long-running branches (one branch per program, accumulating commits across many iterations). Every iteration after the first creates a fresh, disconnected branch with a new salt suffix, producing a growing pile of unrelated PRs instead of a single PR accumulating commits.
Have the agent create a PR on branch my-branch. This works — PR opens on my-branch.
Merge or close the PR without deleting the branch (the branch remains on the remote).
On the next run, have the agent create another PR on my-branch.
Observed: gh-aw emits ##[warning]Remote branch my-branch already exists - appending random suffix, renames the branch to my-branch-abc12345, and opens a new PR there.
Expected: honor preserve-branch-name: true — either push onto the existing branch (creating a new PR pointing at the same branch, or failing cleanly if no open PR exists), or fail explicitly with an actionable error. Silent suffix-appending contradicts the documented behavior.
Example log excerpt
From a real run on a repo using autoloop-style long-running branches:
Using branch name from JSONL without salt suffix (preserve-branch-name enabled): autoloop/perf-comparison
Generated branch name: autoloop/perf-comparison
Base branch: main
Fetching base branch: main
Branch should not exist locally, creating new branch from base: autoloop/perf-comparison
Switched to a new branch 'autoloop/perf-comparison'
Created new branch from base: autoloop/perf-comparison
##[warning]Remote branch autoloop/perf-comparison already exists - appending random suffix
[command]/usr/bin/git branch -m autoloop/perf-comparison autoloop/perf-comparison-36d7559a
Renamed branch to autoloop/perf-comparison-36d7559a
Root cause
In actions/setup/js/create_pull_request.cjs:
Line 294: const preserveBranchName = config.preserve_branch_name === true;
Line 822: branchName = normalizeBranchName(branchName, preserveBranchName ? null : randomHex); — flag is honored during initial normalization.
Line 829: if (preserveBranchName) { ... } — logs the "without salt suffix" message.
Collision handling appears three times (signed-commits path ~L985, patch-apply path ~L1213, empty-commit path ~L1377). All three blocks are identical and do not reference preserveBranchName:
Gate each of the three collision blocks on !preserveBranchName. When preserveBranchName is true and the remote branch already exists, pick one of:
Push onto the existing branch (matching the "long-running branch" intent users typically have when enabling this flag). If an open PR already points at the branch, add the commit there; if not, open a new PR targeting the existing branch.
Fail loudly with a descriptive error naming the colliding branch and suggesting either deleting it or using push-to-pull-request-branch instead. Silent bypass is the worst option because the user sees a PR on a differently-named branch with no visible error — the flag appears to work initially and then silently stops.
At minimum, document the current behavior in the preserve-branch-name reference doc so users aren't surprised.
Docs: docs/src/content/docs/reference/safe-outputs-pull-requests.md — describes preserve-branch-name as "omits the random hex salt suffix that is normally appended" with no mention of collision handling.
Summary
safe-outputs.create-pull-request.preserve-branch-name: trueis silently bypassed when the agent-supplied branch name already exists on the remote. The collision handler unconditionally appends a random 8-char hex salt (-[0-9a-f]{8}) to the branch name, defeating the feature's stated purpose.This is particularly painful for workflows that use long-running branches (one branch per program, accumulating commits across many iterations). Every iteration after the first creates a fresh, disconnected branch with a new salt suffix, producing a growing pile of unrelated PRs instead of a single PR accumulating commits.
Reproducer
my-branch. This works — PR opens onmy-branch.my-branch.##[warning]Remote branch my-branch already exists - appending random suffix, renames the branch tomy-branch-abc12345, and opens a new PR there.preserve-branch-name: true— either push onto the existing branch (creating a new PR pointing at the same branch, or failing cleanly if no open PR exists), or fail explicitly with an actionable error. Silent suffix-appending contradicts the documented behavior.Example log excerpt
From a real run on a repo using autoloop-style long-running branches:
Root cause
In
actions/setup/js/create_pull_request.cjs:const preserveBranchName = config.preserve_branch_name === true;branchName = normalizeBranchName(branchName, preserveBranchName ? null : randomHex);— flag is honored during initial normalization.if (preserveBranchName) { ... }— logs the "without salt suffix" message.Collision handling appears three times (signed-commits path ~L985, patch-apply path ~L1213, empty-commit path ~L1377). All three blocks are identical and do not reference
preserveBranchName:So the salt is appended regardless of the flag.
Suggested fix
Gate each of the three collision blocks on
!preserveBranchName. WhenpreserveBranchNameis true and the remote branch already exists, pick one of:push-to-pull-request-branchinstead. Silent bypass is the worst option because the user sees a PR on a differently-named branch with no visible error — the flag appears to work initially and then silently stops.At minimum, document the current behavior in the
preserve-branch-namereference doc so users aren't surprised.Context / references
preserve-branch-nameoption tocreate-pull-requestsafe output #20788 (merged 2026-03-13)docs/src/content/docs/reference/safe-outputs-pull-requests.md— describespreserve-branch-nameas "omits the random hex salt suffix that is normally appended" with no mention of collision handling.pkg/workflow/create_pull_request.go—PreserveBranchNamefield.Happy to submit a PR gating the three collision blocks on
!preserveBranchNameif that's the preferred direction.