Skip to content

Tag mode: setupBranch() fetches before configureGitAuth(), so persist-credentials:false breaks every mention on a private repo #1711

Description

@johanhanses

Summary

In tag mode, setupBranch() runs git fetch before configureGitAuth(). On a private repository checked out with actions/checkout's persist-credentials: false, there is no credential for that fetch, so the action dies in setup — after it has already posted its "Claude Code is working…" tracking comment, which then never resolves.

The result is a permanently pending comment on the PR and no review, for every mention.

Repro

Workflow (private repo):

on:
  issue_comment: { types: [created] }
jobs:
  claude:
    runs-on: ubuntu-latest
    permissions: { contents: read, pull-requests: write, issues: write }
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 1
          persist-credentials: false     # <- the trigger
      - uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
          # no `prompt:` -> tag mode

Comment @claude review this on an open same-repo PR.

Action failed with error: Command failed: git fetch origin --depth=20 <branch>

Cause (v1)

  • src/modes/detector.ts — a comment event with no prompt input and a matched trigger phrase resolves to "tag".
  • src/modes/tag/index.ts — posts the tracking comment, then calls setupBranch(...) at line 67; configureGitAuth(...) is called only afterwards, at lines 84 / 97.
  • src/github/operations/branch.ts:212 — for an open, same-repo PR: execGit(["fetch", "origin", ...depthArgs, branchName]), unconditionally. There is no check for whether the requested ref is already present in the working copy.

So the ordering is: tracking comment → unauthenticated fetch → (never reached) git auth.

Agent mode is unaffected: src/modes/agent/index.ts never calls setupBranch, and reads branch state from GITHUB_HEAD_REF / GITHUB_REF_NAME instead. That is why a workflow_dispatch-triggered workflow using the identical checkout config works fine — it takes a different code path, not a safer configuration.

Why persist-credentials: false is not simply user error

actions/checkout writes the job token into .git/config as an http.extraheader. A workflow that runs an agent with file-read tools over the checkout is handing that agent its own token — so persist-credentials: false is the correct hardening for exactly this action's use case, not an exotic choice. Right now that hardening is incompatible with tag mode.

Pinning actions/checkout's ref: to the PR head does not work around it: setupBranch fetches by branch name regardless of what is already checked out. (Verified — that was our first attempted fix.)

Suggested fixes, roughly in order of preference

  1. Configure git auth before setupBranch(), so the fetch it performs is authenticated. Smallest change, fixes the ordering bug directly.
  2. Skip the fetch when the requested ref is already checked out — cheap, and a speedup for every consumer that pins ref:.
  3. Failing either, fail before posting the tracking comment, so a setup crash does not leave a permanently pending "Claude Code is working…" on the PR. The dangling comment is arguably the worse half of this bug: it reads as "still running" forever.

Environment

anthropics/claude-code-action@v1, actions/checkout@v7, ubuntu-latest, private repository, tag mode (no prompt: input).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:permissionsbugSomething isn't workingduplicateThis issue or pull request already existsp2Non-showstopper bug or popular feature request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions