Problem
When GITHUB_TOKEN expires mid-session, calling refresh_credentials (the MCP tool) refreshes the backend credentials — the git credential helper (/tmp/git-credential-ambient) returns a valid fresh token — but the GITHUB_TOKEN shell environment variable retains the stale value. Since the gh CLI prioritizes GITHUB_TOKEN over all other credential sources, every gh command fails with 401 Bad credentials even though valid credentials are available.
This affects all gh operations: gh pr create, gh api, gh repo list, gh issue view, gh search issues, etc.
Steps to reproduce
- Start a session with GitHub integration connected
- Wait for the
GITHUB_TOKEN to expire (or observe it mid-session)
- Call
refresh_credentials MCP tool — it reports success
- Run
gh auth status — fails with "The token in GITHUB_TOKEN is invalid"
- Verify the credential helper has a fresh token:
FRESH_TOKEN=$(printf 'protocol=https\nhost=github.com\n\n' | git credential fill 2>/dev/null | grep '^password=' | cut -d= -f2)
echo "Token length: ${#FRESH_TOKEN}" # Returns 40, confirming a token exists
- Override the env var inline — works:
GITHUB_TOKEN="$FRESH_TOKEN" gh auth status # Succeeds
- But the next
gh command in a new shell invocation fails again because GITHUB_TOKEN is re-injected with the stale value
Expected behavior
After refresh_credentials is called, gh commands should work. Either:
- The
GITHUB_TOKEN env var should be updated to the fresh token, or
- The stale
GITHUB_TOKEN should be cleared so gh falls through to the credential helper
Actual behavior
GITHUB_TOKEN retains the expired value across all shell invocations
gh reads the stale env var and returns 401 on every call
- The credential helper has a valid token but
gh never consults it because GITHUB_TOKEN takes precedence
Impact
- PR creation fails (both
gh pr create and searching for existing forks/issues)
- No
gh api calls work, blocking any GitHub API interaction
- Workaround exists (inline
GITHUB_TOKEN=fresh gh ... per command) but is fragile since fresh tokens also appear to be short-lived
Workaround
The updated PR skill in workflows/bugfix/.claude/skills/pr/SKILL.md documents this workaround:
FRESH_TOKEN=$(printf 'protocol=https\nhost=github.com\n\n' | git credential fill 2>/dev/null | grep '^password=' | cut -d= -f2)
GITHUB_TOKEN="$FRESH_TOKEN" gh auth status
This works for individual commands but must be repeated for each invocation because the shell env is re-initialized with the stale token each time.
Relationship to #935
Issue #935 addresses the type of token (installation tokens lack cross-fork PR permissions). This issue addresses the delivery of the token (the env var goes stale mid-session). They are complementary — even with #935's proposed OAuth user-to-server tokens, those tokens would still go stale if the env var isn't synced after refresh.
This issue was filed by Claude Code under the supervision of Bill Murdock.
Problem
When
GITHUB_TOKENexpires mid-session, callingrefresh_credentials(the MCP tool) refreshes the backend credentials — the git credential helper (/tmp/git-credential-ambient) returns a valid fresh token — but theGITHUB_TOKENshell environment variable retains the stale value. Since theghCLI prioritizesGITHUB_TOKENover all other credential sources, everyghcommand fails with401 Bad credentialseven though valid credentials are available.This affects all
ghoperations:gh pr create,gh api,gh repo list,gh issue view,gh search issues, etc.Steps to reproduce
GITHUB_TOKENto expire (or observe it mid-session)refresh_credentialsMCP tool — it reports successgh auth status— fails with "The token in GITHUB_TOKEN is invalid"ghcommand in a new shell invocation fails again becauseGITHUB_TOKENis re-injected with the stale valueExpected behavior
After
refresh_credentialsis called,ghcommands should work. Either:GITHUB_TOKENenv var should be updated to the fresh token, orGITHUB_TOKENshould be cleared soghfalls through to the credential helperActual behavior
GITHUB_TOKENretains the expired value across all shell invocationsghreads the stale env var and returns 401 on every callghnever consults it becauseGITHUB_TOKENtakes precedenceImpact
gh pr createand searching for existing forks/issues)gh apicalls work, blocking any GitHub API interactionGITHUB_TOKEN=fresh gh ...per command) but is fragile since fresh tokens also appear to be short-livedWorkaround
The updated PR skill in
workflows/bugfix/.claude/skills/pr/SKILL.mddocuments this workaround:This works for individual commands but must be repeated for each invocation because the shell env is re-initialized with the stale token each time.
Relationship to #935
Issue #935 addresses the type of token (installation tokens lack cross-fork PR permissions). This issue addresses the delivery of the token (the env var goes stale mid-session). They are complementary — even with #935's proposed OAuth user-to-server tokens, those tokens would still go stale if the env var isn't synced after refresh.
This issue was filed by Claude Code under the supervision of Bill Murdock.