[BUG] GitHub repo-scoping gateway returns 403 on github.com even with "All domains" egress, breaking Bazel dependency resolution

Open 💬 0 comments Opened Jul 14, 2026 by bjencks

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In a Claude Code cloud/web session with network egress set to "All domains"
(full outbound access), HTTP fetches to github.com for any repository outside
the session's granted scope are denied with HTTP 403 by the Claude Code
GitHub integration gateway — not by the egress policy. This affects plain,
unauthenticated, public artifact downloads (release assets, source archives), so
it breaks any build tool that resolves dependencies from GitHub. A minimal Bazel
build cannot fetch even its base modules; buck2 and others are affected the same
way.

Two things make this surprising:

  1. "All domains" egress does not imply unrestricted github.com. General

outbound is open (pypi.org, mirror.bazel.build, raw.githubusercontent.com
all return 200), but github.com, codeload.github.com, and
api.github.com are intercepted by a repo-scoping gateway that returns 403
for out-of-scope repos with *"GitHub access to this repository is not enabled
for this session. Use add_repo to request access."*

  1. The gate is path/protocol-specific, not a blanket block. The git wire

protocol (git-upload-pack) is allowed for the same out-of-scope public
repos — git clone succeeds — while the HTTP artifact/API paths
(/releases/download/, /archive/, codeload tarballs, api.github.com)
are 403-gated. Public read-only access is already permitted for one protocol
but denied for the other. This is exactly the path build tools use to fetch
dependencies, so git-based workflows can look fine while
http_archive/release-asset fetches fail.

What Should Happen?

When egress is set to "All domains", read-only public GitHub artifact downloads
should succeed — consistent with raw.githubusercontent.com and the git wire
protocol, both of which already pass through. Specifically:

  • github.com / codeload.github.com read-only artifact paths

(/releases/download/, /archive/, codeload tarballs) should return the
asset for public repos, not a repo-scoping 403.

  • At minimum, the behavior should be consistent across protocols: if git clone

of a public out-of-scope repo is allowed, fetching that repo's release/archive
assets over HTTP should be allowed too.

Alternatives that would also resolve it: provide a complete, already-allowed
BCR-backed artifact mirror and document the --experimental_downloader_config
rewrite targeting it; or, at minimum, document clearly that "All domains" egress
does not lift the GitHub repo-scoping gateway and that add_repo / the
partial mirror.bazel.build rewrite are the current workarounds.

Error Messages/Logs

Gateway 403 body (this is the gateway's response, not GitHub's):


{"message":"GitHub access to this repository is not enabled for this session. Use add_repo to request access.",
 "documentation_url":"https://docs.anthropic.com/en/docs/claude-code/github-actions"}


Minimal Bazel build failure — a module whose only target is a `genrule` still
fails, because it pulls `@platforms//host` from a blocked `github.com` release:


ERROR: An error occurred during the fetch of repository 'platforms':
  Error downloading [https://github.com/bazelbuild/platforms/releases/download/0.0.9/platforms-0.0.9.tar.gz]:
  GET returned 403 Forbidden
ERROR: no such package '@@platforms//host': ...


Status codes by host, probed against an out-of-scope public repo
(`bazelbuild/platforms`), egress = "All domains":

| Host | Path | Result | Repo-scope gated? |
|---|---|---|---|
| `github.com` | `/releases/download/...` | 403 (GET **and** HEAD) | yes |
| `github.com` | `/archive/refs/tags/...` | 403 | yes |
| `codeload.github.com` | `/tar.gz/refs/tags/...` | 403 | yes |
| `api.github.com` | `/repos/...` | 403 | yes |
| `github.com` | `git-upload-pack` (`git clone`) | **works (exit 0)** | no |
| `raw.githubusercontent.com` | `/<repo>/<ref>/<file>` | **200** | no (passes through) |
| `mirror.bazel.build` | (mirrored archive) | 200 | no (non-github) |
| `pypi.org` | `/simple/` | 200 | no (non-github) |
| `github.com` | in-scope repo (`bjencks/ai-scratch`) | 404, not 403 | no (passes the gate) |

Steps to Reproduce

Session: Claude Code web, egress = "All domains". Bazel 7.4.1 via bazelisk.

# 1. Out-of-scope public repo — gateway 403 on BOTH GET and HEAD, at the first hop:
$ curl -s  -o /dev/null -w "%{http_code}\n" https://github.com/bazelbuild/platforms/releases/download/0.0.9/platforms-0.0.9.tar.gz
403
$ curl -sI -o /dev/null -w "%{http_code}\n" https://github.com/bazelbuild/platforms/releases/download/0.0.9/platforms-0.0.9.tar.gz
403
$ curl -s https://github.com/bazelbuild/platforms/releases/download/0.0.9/platforms-0.0.9.tar.gz
{"message":"GitHub access to this repository is not enabled for this session. Use add_repo to request access.", ...}

# 2. Same gating on codeload and api; raw is NOT gated; general egress is open:
$ curl -s -o /dev/null -w "%{http_code}\n" https://codeload.github.com/bazelbuild/platforms/tar.gz/refs/tags/0.0.9   # 403
$ curl -s -o /dev/null -w "%{http_code}\n" https://api.github.com/repos/bazelbuild/platforms                        # 403
$ curl -s -o /dev/null -w "%{http_code}\n" https://raw.githubusercontent.com/bazelbuild/platforms/0.0.9/BUILD       # 200
$ curl -s -o /dev/null -w "%{http_code}\n" https://pypi.org/simple/                                                 # 200

# 3. In-scope repo passes the gate (normal GitHub 404, not the gateway 403):
$ curl -s -o /dev/null -w "%{http_code}\n" https://github.com/bjencks/ai-scratch/archive/refs/heads/main.tar.gz     # 404

# 4. But the git wire protocol is NOT gated — cloning the same out-of-scope public repo works:
$ git clone --depth 1 https://github.com/octocat/Hello-World.git   # Cloning into 'Hello-World'... (exit 0)

# 5. Minimal Bazel repro — fails fetching @platforms//host:
$ cat > MODULE.bazel <<'EOF'
module(name = "repro", version = "0")
EOF
$ cat > BUILD.bazel <<'EOF'
genrule(name = "hello", outs = ["hello.txt"], cmd = "echo hi > $@")
EOF
$ bazel build //:hello
# ERROR: ... Error downloading [.../platforms-0.0.9.tar.gz]: GET returned 403 Forbidden

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Claude Code 2.1.209

Platform

Anthropic API

Operating System

Other

Terminal/Shell

Other

Additional Information

Tools affected

  • Bazel (bzlmod / http_archive): the Bazel Central Registry resolves

nearly every module's source archive from github.com release assets, so
dependency resolution fails immediately.

  • Tool installation itself, before any build. GitHub releases are the

primary distribution channel for the core Bazel tooling — bazelisk
(bazelbuild/bazelisk) and buildifier/buildozer
(bazelbuild/buildtools). Their documented install is the release binary on
github.com, which is blocked. go install works as a fallback (the Go
module proxy is allowed) but is non-obvious and needs a Go toolchain.

Why the common workarounds don't fully solve it

  • add_repo does not scale: a realistic Bazel build pulls dozens of distinct

GitHub-hosted repos (rules_cc, rules_java, rules_python, abseil, protobuf,
bazel-contrib/, aspect-build/, …), each needing a separate grant. It also
refuses cross-owner adds when the session already has a source from another
owner.

  • Rewriting fetches to mirror.bazel.build via

--experimental_downloader_config helps for some core modules but is
incomplete — many recent releases are unmirrored. Resolving the latest
rules_oci (2.3.0) graph, every archive below is 403 on github.com and
404 on the mirror:

| Module | Version |
|---|---|
| rules_oci | v2.3.0 |
| aspect_bazel_lib | v2.21.1, v3.0.0 |
| tar.bzl | v0.7.0 |
| bazel_features | v1.34.0 |
| rules_shell | v0.4.1 |
| rules_jvm_external | 6.1 |
| rules_cc | 0.1.1 |
| rules_python | 0.23.1 |

Root cause (analysis)

The Claude Code GitHub integration gateway intercepts github.com,
codeload.github.com, and api.github.com and enforces per-session repository
scoping, returning 403 for any repo not granted to the session — applied
independently of the network egress policy, so it stays active under "All
domains". The gate is inconsistent across protocols: git-upload-pack (git
clone) is allowed for out-of-scope public repos, while the HTTP artifact/API
paths for the same repos are 403.

Related issues (checked; this is distinct from all of them)

requests denied by the github proxy. Also "All domains", but there
github.com GET returns 200 and only HEAD fails with 401 at the
presigned release-assets.githubusercontent.com redirect target. Here
github.com itself is repo-gated, so even GET is 403 at the first hop.
Different layer.

credential-injection proxy returns 502. Same sandbox environment and it
observes the same 403-for-public-repos, so it is the closest relative — but
its headline is a 502 "builtin injection failed" infra failure blocking git
auth, not the "All domains" HTTP-artifact-gating angle. Worth cross-linking.

#71542 — the
claude.ai Chat GitHub connector. Different product surface, not the code
sandbox egress path.

git clone failing (exit 128). That is the git-protocol path, which
succeeds in this environment; our failure is the HTTP artifact/API path.

Environment

  • Claude Code web / remote execution environment; egress policy: All domains.
  • Bazel 7.4.1 via bazelisk (bazelisk installed with go install, since its

release binary on github.com is also gated).

  • Allowed hosts observed: releases.bazel.build, mirror.bazel.build,

bcr.bazel.build, raw.githubusercontent.com, proxy.golang.org, pypi.org.

  • Gated hosts observed: github.com, codeload.github.com, api.github.com

(out-of-scope repos → 403 on HTTP artifact/API paths).

View original on GitHub ↗