claude-code - 💡(How to fix) Fix `gh pr create` sends empty Authorization header inside Claude Code session (HTTP 401) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#52664Fetched 2026-04-24 10:43:01
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×4

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.

Error Message

  1. Fails with: error checking for existing pull request: HTTP 401: Requires authentication (https://api.github.com/graphql)

Root Cause

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.

Fix Action

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.

Code Example

GH_DEBUG=api gh pr create --repo IyadhKhalfallah/clauditor ...

---

* 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

---

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'])
RAW_BUFFERClick to expand / collapse

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 (the Agent/claude-code suffix is the hint that the claude-code integration layer is involved)
  • Auth scopes present: gist, read:org, repo, workflow
  • No GH_TOKEN or GITHUB_TOKEN env vars set

Repro

  1. gh auth login -h github.com (HTTPS + web flow)
  2. gh auth status shows logged in; gh api user --jq .login returns the username
  3. gh api graphql -f query='query { viewer { login } }' returns {"data":{"viewer":{"login":"..."}}}
  4. gh pr create --repo <owner>/<repo> --base main --head <fork>:<branch> --title test --body test
  5. 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

extent analysis

TL;DR

The issue can be worked around by using the REST API directly and passing the token grabbed from gh auth token, as the gh pr create command is not sending the Authorization header when invoked from inside a Claude Code session.

Guidance

  • The problem seems to be related to the Claude Code integration layer intercepting some subcommands and dropping the Authorization header.
  • To verify the issue, run GH_DEBUG=api gh pr create and check the debug output for an empty Authorization: header.
  • As a temporary workaround, use the provided Python script or the gh api command with the -X POST option to create a pull request.
  • The gh auth token command can be used to obtain the token and pass it to the REST API directly.

Example

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'])

Notes

The root cause of the issue is likely related to the Claude Code integration layer, but the exact cause is unclear. The provided workaround should help mitigate the issue until a proper fix is available.

Recommendation

Apply the workaround using the REST API directly, as it allows for creating pull requests without relying on the `gh pr create

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix `gh pr create` sends empty Authorization header inside Claude Code session (HTTP 401) [1 participants]