Claude Codeのクラウドセッションでタグをpushすると403になる原因と回避策
クラウドセッションでブランチはpushできるのにタグだけ403になる。GitHub App権限や保護設定を確認しても直らない理由を、docsのGitHubプロキシの記述とissueの報告から切り分けます。
Claude Codeのクラウドセッションで git push origin v1.3.0 を実行すると、HTTP 403 で止まる。同じセッションで git push origin main は通る。GitHub Appの権限もタグの保護設定も問題ないのに、です。
原因として最も筋が通るのは、リポジトリ側の設定ではなくクラウドセッションのGitHubプロキシです。docsには、ブランチ以外のref(タグなど)のpushをプロキシが拒否すると書かれています。この記事では報告の中身、リポジトリ側で潰せる原因、タグが必要なときの逃げ道を順に扱います。
報告されている症状はどんな形か
GitHubのissue(claude-code#95576)に、クラウドセッションからのタグpushが失敗するという報告があります。起票者の環境は、Claude Code 2.1.278、GitHub Appは「All repositories」に導入済み、リポジトリは個人所有です。
セッション内で起きたことは次の表のとおりです。
| 操作 | 結果 |
|---|---|
git push origin main | 結果成功 |
git push origin v1.3.0(注釈付きタグ) | 結果403 |
| 2秒から16秒まで間隔を空けた4回の再試行 | 結果毎回403 |
別名のタグ test-tag-delete-me | 結果同じく403 |
出力はこの形でした。
git push origin v1.3.0
error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedlyタグ名を v*.*.* 以外に変えても同じ結果だったので、名前のパターンによる規則ではありません。再試行で変わらない点から、一時的なネットワーク障害でもないと分かります。
issueには、同じ環境で8月には通っていたという記述があります。コメントは2件で、1件は「前日から同じ症状が出始めた」、もう1件は「診断に1時間かかった。具体的なフィードバックがない」という内容です。
リポジトリ側で潰せる原因は何か
起票者は、GitHub側で次の3つを確認し、どれも該当しないと報告しています。
- 従来型のタグ保護(SettingsのTags)が空
- リポジトリルールセット(SettingsのRules)が存在しない
- Claude GitHub Appの権限が、コード、ワークフローなどに対する読み書き
この3点が揃うなら、リポジトリ側にブランチとタグを分ける要因はありません。権限や保護を何度見直しても直らないときは、見る場所を変える合図です。
セッション内のプロキシ状態も手がかりになります。起票者が見たローカルのステータス(/__agentproxy/status)では、recentRelayFailures が空でした。起票者はこれを「GitHub自身が返した403」と読んでいます。ただし、プロキシが意図的に返す拒否がこの記録に出るのかは、issueから分かりません。断定は避け、次のdocsの記述と合わせて読みます。
docsのGitHubプロキシはタグpushをどう扱うか
Anthropicがホストする環境では、GitHubへの操作はすべて専用のプロキシを通ります。実際のGitHub認証情報はVMの外に置かれ、VMのgitクライアントは範囲を絞った資格情報を使います。docsはこのプロキシの機能の一つとして、Push restrictionsを挙げています。
the proxy rejects branch deletions and pushes of anything other than a branch, such as a tag.
訳すと「ブランチの削除と、ブランチ以外(タグなど)へのpushをプロキシが拒否する」です。どのブランチを更新できるかは制限しないので、そちらはGitHubのブランチ保護やルールセットで絞るよう書かれています。
症状とこの記述は噛み合います。ブランチは通り、タグは落ちる。タグ名を変えても同じ。リポジトリ側に原因が見つからない。
起票者は「以前は通った」ことを根拠に退行を疑っています。docsの記述がいつから現行の形なのかは、docsからは分かりません。意図した制限なのか、制限の適用が途中から厳密になったのかは言い切れません。起票者もissueの末尾で、意図的な制限なら素の403でなく明示的なエラーにしてほしいと求めています。
もう一つ、起票者はGitHub MCPツールの観察を添えています。セッションにはタグやリリースを読むツール(get_tag、list_tags、get_release_by_tag)はあっても、作るツールがありません。ブランチ、ファイル、PRには書き込み系のツールがあります。タグ作成だけ意図的に外されているなら製品判断として自然ですが、事前に知る手段が403しかない点を問題にしています。
タグが必要なときの回避策はどれか
起票者自身の回避策は、GitHubのWeb画面でリリースを作ることです。リリースの作成でタグも作られるので、タグのpushを契機にするCIはそのまま動きます。
ほかに、リポジトリ側で用意できる形が3つあります。どれもクラウドセッションの外で完結し、プロキシの制限には触れません。
タグを作る場所を変える3案
手元のgh
gh release create v1.3.0 --target main --generate-notes を自分の端末で実行します。リリースとタグが同時にできます。
手元のgit
セッションがmainへpushしたあと、自分の端末で git fetch し、git tag -a と git push origin <tag> を実行します。
workflow_dispatch
タグを作るワークフローを手動実行用に足します。リリース作業がセッションと切り離され、Web画面のボタンから動かせます。
三つ目の形は、たとえば次のように書けます。バージョンを入力にして、mainの先頭にタグを打つ例です。
name: tag-release
on:
workflow_dispatch:
inputs:
version:
description: "例: v1.3.0"
required: true
permissions:
contents: write
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main
- run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "${{ inputs.version }}" -m "${{ inputs.version }}"
git push origin "${{ inputs.version }}"git tag -a は注釈付きタグなので、committerの名前とメールアドレスが要ります。actions/checkout はこれを設定しないため、git config の2行が無いと「Please tell me who you are」で失敗します。設定を省くなら、軽量タグ(git tag "${{ inputs.version }}")に変える手もあります。
起票者のリポジトリは、タグのpushだけを契機にしたリリースワークフローで、workflow_dispatch の入口がありませんでした。この構成には落とし穴があります。GITHUB_TOKEN でpushしたタグは、別のワークフローを起動しません。GitHub Actionsの仕様で、例外は workflow_dispatch と repository_dispatch だけです。つまり上のtag-releaseで打ったタグからは、タグpush起点のリリースワークフローが動きません。
対処は3つあります。
- リリース処理をtag-releaseと同じワークフローに入れる
- 既存のリリースワークフローに
workflow_dispatchを足し、tag-releaseの最後から呼ぶ GITHUB_TOKENでなく、PATかGitHub Appのトークンでpushする
いちばん単純なのは1です。トークンを増やさず、権限も contents: write のままで済みます。
クラウドセッションの中から gh api でタグrefやリリースをREST経由で作れるかは、docsに記載がありません。GraphQLはプロキシが403で拒否すると書かれています。試すなら、失敗しても作業が止まらない場面にしてください。
ブランチの削除も同じ理由で拒否される
先のdocsの記述は、ブランチ以外のpushと並べて「ブランチの削除」も拒否の対象に挙げています。マージ済みブランチを git push origin --delete <branch> で掃除させようとして403になるのは、タグと同じ仕組みです。
クラウドセッションの中では、リモートブランチの掃除もタグ打ちも人の作業として残ります。掃除は手元の端末かGitHubのWeb画面で行い、CLAUDE.mdにも「ブランチは削除しない」と書いておけば、Claudeが削除を再試行する空回りを避けられます。
Claude Codeにタグを触らせないための書き方
403を見たClaudeは、再試行したり別のタグ名を試したりしがちです。起票者の報告でも、間隔を延ばして4回、別名で1回、時間を置いてさらに1回と、同じ失敗を繰り返しています。最初から「タグは作らない」と伝えておけば、この空回りを省けます。
CLAUDE.mdには、次のような断片を置けます。
## リリース作業
- クラウドセッションではタグのpushとブランチの削除ができない。タグ・リリースの作成とブランチ削除は試みない。
- mainへのマージまでを行い、「タグは手動で作成が必要」と報告して終了する。
- リリースは Actions の tag-release (workflow_dispatch) から人が実行する。「失敗したら再試行する」という一般的な挙動を、「このリポジトリではタグは人の作業」という規約で上書きする形です。リリース手順をスキルにまとめている場合も、タグの手順だけは人に渡す箇所として分けておくと、途中で止まった理由が読み取れます。
タグ以外の403と見分けるには
クラウドセッションのGitHub周りで出る403は、メッセージの頭で原因が分かれます。docsに載っている文言は次の2つです。
| 403のメッセージ | 意味 |
|---|---|
GitHub access to ... is not enabled for this session | 意味セッションに接続していない別リポジトリへのAPI要求、または未接続リポジトリのアーカイブ取得 |
GitHub GraphQL is not available from Claude Code sessions | 意味GraphQL経由の要求。gh pr や gh issue も含む |
どちらでもない素の HTTP 403 curl 22(git pushの形)なら、まず「ブランチ以外のrefをpushしていないか」を確かめます。ブランチ削除も同じ拒否の対象です。GitHub Appの設定を延々と見直す前に、pushしているrefの種類を見るのが先です。
セッションそのものが作れない失敗は、Cloud sessions拒否の切り分けで扱っています。auto modeでローカルのpushが止まる場合はauto modeのpushブロックが近い症状です。GitHub未接続のリポジトリをクラウドに送る仕組みはclaude --cloudとCCR_FORCE_BUNDLEに、ほかのクラウドエージェントとの権限の違いはCopilot cloud agentとの比較にあります。
まとめ
ブランチはpushできるのにタグだけ403になるなら、GitHubの設定より先に、プロキシがブランチ以外のpushを拒否するというdocsの記述を疑います。リポジトリ側を見直しても直らないのは、そこに原因がないからです。
タグ打ちは、手元のgh、手元のgit、workflow_dispatch のいずれかに分けます。以前は通ったという報告があり、挙動が変わる可能性は残ります。それまでは、CLAUDE.mdに「タグは人が作る」と書いてClaudeの再試行を止めておく運用が無難です。