`gh pr create` sends empty Authorization header inside Claude Code session (HTTP 401)
Summary
gh pr create (and likely other gh subcommands that hit the GraphQL endpoint via the same code path) sends an empty Authorization: header when invoked from inside a Claude Code session, even though gh auth status reports logged-in and the token is valid. Other gh subcommands (gh api, gh auth token, gh api graphql -f query=...) do pick up the token correctly. This means any gh operation that requires auth through that specific code path fails with HTTP 401: Requires authentication until the user falls back to the REST API directly.
Environment
- Claude Code: 2.1.108 (installed via
brew install --cask claude-code) - gh: 2.91.0 (from
brew install gh) - macOS: 15.6.1 (Darwin 24.6.0)
- User-Agent in failing request:
GitHub CLI 2.91.0 Agent/claude-code(theAgent/claude-codesuffix is the hint that the claude-code integration layer is involved) - Auth scopes present:
gist, read:org, repo, workflow - No
GH_TOKENorGITHUB_TOKENenv vars set
Repro
gh auth login -h github.com(HTTPS + web flow)gh auth statusshows logged in;gh api user --jq .loginreturns the usernamegh api graphql -f query='query { viewer { login } }'returns{"data":{"viewer":{"login":"..."}}}gh pr create --repo <owner>/<repo> --base main --head <fork>:<branch> --title test --body test- Fails with:
error checking for existing pull request: HTTP 401: Requires authentication (https://api.github.com/graphql)
Debug trace
GH_DEBUG=api gh pr create --repo IyadhKhalfallah/clauditor ...
Output (abridged):
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization:
> Content-Length: 526
> Content-Type: application/json; charset=utf-8
> User-Agent: GitHub CLI 2.91.0 Agent/claude-code
> X-Github-Api-Version: 2022-11-28
The Authorization: header is present but empty. A fresh gh api graphql -f query=... run immediately before and after does populate the header correctly.
Expected
gh pr create should use the same token resolution path as gh api, picking up the keyring token and sending Authorization: Bearer ghp_... or Authorization: token ghp_... on the GraphQL request.
Workaround
Call the REST API directly and pass the token grabbed from gh auth token:
import json, urllib.request, subprocess
token = subprocess.check_output(['gh', 'auth', 'token']).decode().strip()
payload = json.dumps({
'title': '...',
'body': '...',
'head': '<fork-owner>:<branch>',
'base': 'main',
}).encode()
req = urllib.request.Request(
'https://api.github.com/repos/<base-owner>/<repo>/pulls',
data=payload,
method='POST',
headers={
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
'User-Agent': 'gh-api-fallback',
},
)
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['html_url'])
This works because it uses the token directly rather than whatever code path gh pr create is going through. Using gh api -X POST repos/<owner>/<repo>/pulls -f title=... -f body=... should also work as a workaround for anyone hitting this without needing to write Python.
Impact
Any automated PR creation from inside a Claude Code session fails silently until the user notices the 401. Since gh auth status reports a healthy login, the failure mode is confusing — it looks like an upstream auth issue rather than a local integration bug.
Related observation
The Agent/claude-code suffix in the User-Agent suggests gh is running through a claude-code-specific wrapper or modified build. The selective-empty-header pattern (some commands fine, pr create not) suggests the wrapper is intercepting some subcommands for permission prompting or other behavior and dropping the Authorization header in the process.
🤖 Filed via Claude Code
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗