Cowork cloud sessions cannot access any GitHub repository, and the git proxy instructs the agent to call a nonexistent add_repo tool

Status Open
Maintainer reply None cached
Activity 3 comments · opened Aug 6, 2026

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?

Cowork cloud sessions cannot access any GitHub repository, and the git proxy's own error message instructs the agent to call a tool (add_repo) that does not exist in the session.

A Cowork cloud session started with no repository configured as a session source has three overlapping blockers, and no discoverable path around any of them. Notably, the failure is not limited to private repositories — the session-bound git proxy blocks all repository access, including public repos that require no credential at all.

This is a fresh report rather than a comment on #76248 because that issue is scoped to pushes failing with PAT pass-through. This report adds two distinct defects: (a) reads are blocked too, not just writes, and (b) the remediation instruction embedded in the proxy's error text points at a nonexistent tool. Also related: #64130, #74542 (OAuth-only connector, no App installation), and #18467 / #57161 / #79083 (personal-account repos invisible despite App install).

---

Defect 1 (primary) — Proxy error instructs the agent to call a tool that does not exist

Repository-scoped API calls from inside the sandbox return:

GitHub access to this repository is not enabled for this session. Use add_repo to request
access. If add_repo answers that read access is already available and you need GitHub API or
write access, call add_repo again with access:"push" to attach the repository with credentials.

There is no add_repo tool in the session. It is not in PATH, and it is not present in the session's tool registry — a ToolSearch query for add_repo returns only unrelated GitHub MCP tools (create_repository, fork_repository, list_repository_collaborators, …). The error text is written as an instruction to the agent, but the agent has no way to act on it, so the model is deterministically routed into a dead end. The linked documentation_url points at the GitHub Actions docs, which describe an unrelated feature.

Defect 2 — All repository access is blocked, including public repos

The user-level endpoint is blocked entirely:

$ curl -H "Authorization: Bearer $GH_TOKEN" "https://api.github.com/user/repos?visibility=private"
{"message":"This GitHub API path is not available: sessions are bound to their configured
repositories. Use repository-scoped endpoints (repos/{owner}/{repo}/...).",
"documentation_url":"https://docs.anthropic.com/en/docs/claude-code/github-actions"}

Repository-scoped endpoints return 403 for every repo tried, and git clone over HTTPS fails because credential.interactive is disabled and the proxy injects no credential:

$ curl -s -o /dev/null -w "%{http_code}" .../repos/<owner>/<repo>   # → 403
$ git clone --depth 1 https://github.com/<owner>/<repo>.git
Cloning into '/tmp/...'...
fatal: could not read Username for 'https://github.com': terminal prompts disabled

This blocks reads of public repositories as well, which cannot be justified on credential-safety grounds — no credential is needed to clone a public repo. #76248 reports clones/fetches still working; that is no longer the case here, which suggests the enforcement has widened since that report.

Defect 3 — No way to add a repository as a session source from Cowork

Both the proxy error ("add the repository to the session's sources") and the API error ("Use add_repo") presuppose an affordance that does not exist in the Cowork surface. There is no repo picker, no session-source setting, and no slash command. The user cannot fix this from the product, and neither can the agent.

Contributing factor — connector is OAuth-only, private repos invisible

Separately from the proxy, the GitHub MCP connector in the session is authenticated (get_me → correct login, 93 public repos) but sees zero private repositories:

  • search_repositories("user:<login>") → 75 results, all "private": false
  • search_repositories("user:<login> is:private")total_count: 0
  • get_teams()null

This matches the OAuth-authorized-but-App-not-installed pattern in #64130 and #74542. The net effect is that a user who has authorized GitHub, sees "Connected" in settings, and has granted repository access still has no working path to their own private code from Cowork — the connector can't see it and the proxy won't clone it.

What Should Happen?

  1. The proxy error must not instruct the agent to call a tool that isn't registered in the session. Either expose add_repo as a real tool in Cowork cloud sessions, or replace the text with an accurate, user-actionable instruction (and fix the documentation_url, which currently points at GitHub Actions docs).
  2. Public repository reads should not require session binding. Cloning a public repo needs no credential and should not be gated by the credential-injection allowlist.
  3. Cowork should expose a discoverable way to add a repository to a session's authorized set, mirroring the repo picker in Claude Code on the web.
  4. Connecting GitHub should route through the GitHub App installation + repository-selection flow so the connector can actually see private repos (per #64130).

Steps to Reproduce

  1. Start a Cowork cloud session from the web app with no GitHub repository configured as a source.
  2. Ask Claude to read a private repository you own.
  3. Observe the MCP connector returns only public repos, with is:private searches empty.
  4. In the sandbox, run curl -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user/repos → blocked with the "sessions are bound to their configured repositories" message.
  5. Run the same against repos/{owner}/{repo} for any repo, public or private → 403 with the add_repo instruction.
  6. Search the session's tool registry for add_repo → not found.
  7. git clone any repo over HTTPS → fatal: could not read Username ... terminal prompts disabled.

Environment

  • Product: Cowork cloud session (web client)
  • CLAUDE_CODE_REMOTE_SESSION_ID: cse_011Yr7B3YaJ9HcEuoNBnpvVt
  • CLAUDE_CODE_REMOTE_ENVIRONMENT_TYPE: cloud_default
  • CLAUDE_CODE_REMOTE_HERMETIC_MODE: 0
  • Proxy flags observed: CCR_TEST_GITPROXY=1, CCR_AGENT_PROXY_ENABLED=1, CCR_UPSTREAM_PROXY_ENABLED=1, CCR_EGRESS_GATEWAY_ENABLED=1
  • Injected git config: credential.interactive (disabled), url.https://github.com/.insteadOf
  • gh CLI: not installed in the sandbox (gh: command not found), so the documented gh fallback is unavailable
  • Model: claude-opus-5
  • Date observed: 2026-08-06

Impact

For Cowork specifically this is the difference between "an agent that can work on my codebase" and "an agent that can only work on code I paste in." The security rationale for the proxy is understandable — an agent holding a broad-scope token is an exfiltration risk under prompt injection — but the replacement path was never surfaced in this client, so the practical result is no repository access at all, with an error message that sends the agent chasing a tool that doesn't exist. Competing products solve this with either a pasted PAT or an App install with repo selection at setup time; Cowork currently has neither working end to end.

View original on GitHub ↗

3 Comments

afxjzs · 24 days ago

Corroborating this from a separate Cowork cloud session (claude-opus-5, desktop client, August 6). Same add_repo dead end, same documentation_url pointing at the GitHub Actions docs. Two corrections and two additions from testing it directly.

Correction: git clone over HTTPS does work for public repos

Same session, back to back:

git clone --depth 1 https://github.com/afxjzs/doug-is.git   # exit 0, files present

git clone --depth 1 https://github.com/<org>/<private-repo>.git
# fatal: could not read Username for 'https://github.com': terminal prompts disabled

So the git-protocol block isn't blanket. It's specifically that no credential is injected for private repos.

Addition: the REST API is 403 for everything, including public endpoints

curl -o /dev/null -w "%{http_code}" https://api.github.com/repos/afxjzs/doug-is
# 403

curl -o /dev/null -w "%{http_code}" https://api.github.com/repos/anthropics/claude-code/issues/84581
# 403

curl -o /dev/null -w "%{http_code}" https://api.github.com/user/repos
# 403

That second one is the surprising case. Reading a public issue needs no credential and carries no access risk, but it's blocked the same as everything else. The security rationale that justifies the private-repo block doesn't extend to it.

Practical consequence: gh is not installed in the session, and installing it doesn't help, because it speaks to the API that's 403ing. There is no CLI path to GitHub at all. The only working channel is WebFetch against public github.com HTML.

Addition: the desktop-bridge workaround is a dead end, for a specific reason

Cowork can reach a locally-mounted git worktree through the remote-devices bridge, which is a real workaround for reading code. But that shell has no network:

git fetch origin
# fatal: unable to access 'https://github.com/...': Received HTTP code 403 from proxy after CONNECT

The two shells each have exactly one of the two things needed, and there's no way to combine them:

| | network | repo access |
|---|---|---|
| cloud shell | yes | no credential |
| device shell | no | yes (mounted worktree) |

Neither can fetch, so the agent can't tell whether a branch it's reasoning about is current.

Where this actually costs something

This week I was evaluating moving our agent work-order briefs out of in-repo .md files and into GitHub Issues. It's otherwise the better design and removes a class of race condition. I declined it specifically because the agent can't read Issues on a private repo, so the corpus of prior decisions would go dark to it.

That's a case where this limitation changed an architecture decision rather than just being an inconvenience.

---

Adding a repo picker plus routing auth through the GitHub App installation flow, as proposed above, would resolve all of it. Allowing unauthenticated reads of public API endpoints looks decoupled from that and shippable on its own, since there's no credential involved either way.

jmburke · 5 days ago

Also hitting this. DealRoomLabs, private org repo, Cowork cloud session,
claude-opus-5, 2026-08-25. Still reproduces three weeks after the original report.

Task was trivial: create a skills/ folder in DealRoomLabs/[MY_REPO]. Session had
GH_TOKEN and GITHUB_TOKEN set, gh not installed.

git clone https://github.com/DealRoomLabs/[MY_REPO].git
fatal: could not read Username for 'https://github.com': terminal prompts disabled

curl -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/repos/DealRoomLabs/[MY_REPO]
{"message":"GitHub access to this repository is not enabled for this session.
Use add_repo to request access..."}

add_repo is not in the tool registry. ToolSearch returns no match for it either,
so the error is pointing at something the agent has no way to call.

Workaround was to drive the GitHub web UI through the Claude in Chrome tools and
commit from the browser. Fine for one file, unusable past that.

Impact for us: Cowork is where our product and GTM work happens, and a lot of it
touches internal repos. That work now has to move to the Claude Code CLI or get
done by hand. A repo picker in Cowork session settings, matching what Claude Code
on the web already has, would close this.

marcfunos · 1 day ago

Reproducing this on 2026-08-30. Adding a measured matrix.

<html><head></head><body><h1>Cowork cloud sessions: the <code>ccr-gitengine</code> proxy blocks GitHub App installation tokens and rewrites API identity</h1>
<p><strong>Reporter:</strong> Funos (funos.es) — 3 engineers-equivalent, Claude Max + Anthropic API customer
<strong>Surface:</strong> Claude Cowork (cloud sandbox), <code>CCR_AGENT_PROXY_ENABLED=1</code>, <code>CCR_TEST_GITPROXY=1</code>
<strong>First observed:</strong> 2026-08-27 · <strong>Still reproducing:</strong> 2026-08-30
<strong>Related open issues:</strong> anthropics/claude-code #76248 (opened 2026-07-10), #84581 (opened 2026-08-06) — both still open, no maintainer response.</p>
<hr>
<h2>1. Summary</h2>
<p>From inside a Cowork cloud session, three independent behaviours of the outbound proxy make it impossible for us to operate GitHub with a <strong>separate machine identity</strong>:</p>
<ol>
<li><strong><code>api.github.com</code> ignores the credential supplied by the process and answers with the session owner's identity.</strong> <code>GET /user</code> returns HTTP 200 and <code>"login":"marcfunos"</code> with <strong>no</strong> <code>Authorization</code> header at all, and again with a deliberately invalid Bearer token.</li>
<li><strong>The git transport rejects any credential whose prefix is exactly <code>ghs_</code></strong> — the prefix of <em>every</em> GitHub App installation token. One character of difference (<code>GHS_</code>, <code>ghsx_</code>, <code>xghs_</code>) reaches GitHub normally.</li>
<li><strong>Writes are blocked per-repository</strong>, and the error instructs the agent to call a tool named <code>add_repo</code> that <strong>does not exist</strong> in the session's tool list, and that has no corresponding UI control anywhere in Cowork.</li>
</ol>
<p>The same GitHub App installation token works normally from outside the sandbox (HTTP 200, correct HEAD SHA) at the same moment.</p>
<p>We are not asking you to accept our reading of your architecture. We are asking you to explain the observable matrix in §3.</p>
<hr>
<h2>2. Why this matters beyond connectivity</h2>
<p>We deliberately use a GitHub App (<code>funos-claude-techlead</code>) so that automated merges carry <strong>their own identity</strong>, distinct from any human's. That is our audit trail: a merge signed <code>funos-claude-techlead[bot]</code> is an automated action; a merge signed by a person is a human action.</p>
<p>Two consequences of the current behaviour:</p>
<ul>
<li><strong>Attribution.</strong> <code>api.github.com</code> answering with the session owner's login for an unauthenticated request means an agent in a Cowork session acts, at the API layer, <strong>as the human account</strong>. Any action taken that way is indistinguishable in GitHub's audit log from one the human performed.</li>
<li><strong>Governance.</strong> The only credential the proxy will carry is one bound to the session's human identity. Forcing us onto a human PAT collapses machine and human accountability into a single account. That is a compliance regression, not an inconvenience.</li>
</ul>
<hr>
<h2>3. Observable matrix</h2>
<p>All measurements below were taken from inside a Cowork cloud session. <strong>Every token used in the prefix table was fabricated and invalid</strong> — no real credential was needed to produce the behaviour.</p>
<h3>3a. <code>api.github.com</code> — credential is ignored</h3>

Request | Credential sent | Result
-- | -- | --
GET /user | none | HTTP 200, "login":"<session owner>"
GET /user | invalid Bearer | HTTP 200, same login
GET /repos/<org>/<repo> | none | HTTP 403 (see body below)

<p><strong>A meaningless string reaches GitHub. A well-formed GitHub App installation token does not.</strong> The match is case-sensitive, at position 0, on the exact four characters <code>ghs_</code>. A non-existent App ID produces the same result as a valid one, so the outcome does not depend on the token being valid.</p>
<p>Note: the <code>ccr-gitengine</code> 401 does carry a real <code>X-Github-Request-Id</code>, so we make <strong>no claim</strong> about whether GitHub sees the request. What we observe is that the response lacks <code>Server</code>, <code>Date</code> and <code>Content-Length</code>, and that the realm is named after your proxy.</p>
<h3>3c. Writes — blocked per repository, with no available remedy</h3>
<p>Push to a <strong>throwaway repository owned by the reporter</strong>, using a <strong>valid fine-grained PAT with write permission on that repository</strong>:</p>
<pre><code>access denied by the git proxy: &lt;org&gt;/&lt;repo&gt; is not in this session's authorized
repository set, so the proxy will not inject a credential for it. To fix, add the
repository to the session's sources.
</code></pre>
<p>There is no "session's sources" control in the Cowork interface.</p>
<p>Reads via git transport <strong>do</strong> work when a fine-grained read-only PAT is supplied in the URL (1,556 refs returned). Without a credential, <code>git ls-remote</code> fails with <code>could not read Username</code> — i.e. the proxy injects nothing for our repository.</p>
<h3>3d. External control — the same token works outside the sandbox</h3>
<p>At the same time, on the reporter's own machine, with an installation token freshly issued by the same App:</p>
<ul>
<li><code>GET /repos/&lt;org&gt;/&lt;repo&gt;</code> → <strong>HTTP 200</strong></li>
<li><code>git ls-remote</code> → correct <code>HEAD</code> SHA, matching what the sandbox read with a PAT</li>
</ul>
<p>Independent control: a different cloud coding agent (running outside Anthropic's infrastructure) wrote to the <strong>same repository, same organization, same branch protection</strong> throughout the incident. Nothing on our side is broken.</p>
<h3>3e. Sandbox environment (redacted)</h3>
<pre><code>CCR_AGENT_PROXY_ENABLED=1
CCR_UPSTREAM_PROXY_ENABLED=1
CCR_TEST_GITPROXY=1
CCR_EGRESS_GATEWAY_ENABLED=1
https_proxy=http://127.0.0.1:&lt;port&gt;
GH_TOKEN=proxy-injected
GITHUB_TOKEN=proxy-injected
GIT_CONFIG_KEY_1=url.https://github.com/.insteadOf (value: git@github.com:)
GIT_SSL_CAINFO=/root/.ccr/ca-bundle.crt
</code></pre>
<hr>
<h2>4. Questions we would like answered</h2>
<ol>
<li>Is the <code>ghs_</code> prefix filter on the git transport <strong>intended</strong>? If so, what is the supported way for a Cowork session to act under a GitHub App identity?</li>
<li><code>GET /user</code> returning the session owner's login for an <strong>unauthenticated</strong> request: is that intended? What identity actually performs GitHub API calls originating from a Cowork session, and how is that recorded in the customer's GitHub audit log?</li>
<li><code>add_repo</code> is referenced in your own error message. Does it exist? If it exists on another surface, how does a <strong>Cowork</strong> user add a repository to the session's authorized set?</li>
<li>If a repository <em>is</em> attached, <strong>which identity signs the resulting commits and pushes</strong> — the session owner, or a bot/App identity we control?</li>
<li>Is there any supported configuration in which an automated action from a Cowork session is attributable to an identity other than the human who owns the session?</li>
</ol>
<hr>
<h2>5. What we are doing meanwhile</h2>
<p>Read-only auditing from the session with a human fine-grained read-only PAT, and every merge performed manually by a human. This removes the automation benefit but preserves the audit trail. We would prefer a supported path.</p>
<hr>
<h2>6. Reproduction, minimal</h2>
<p>Inside a Cowork cloud session, with <strong>no real credentials</strong>:</p>
<pre><code class="language-bash">curl -s https://api.github.com/user | head -3
curl -s -w '\n%{http_code}\n' https://api.github.com/repos/&lt;any-org&gt;/&lt;any-private-repo&gt;

for t in "ghs_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"GHS_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"ghsx_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"hola-que-tal-1234567890"; do
echo "--- $t"
curl -s -D- -o /dev/null -u "x-access-token:$t" \
"https://github.com/&lt;any-org&gt;/&lt;any-repo&gt;.git/info/refs?service=git-upload-pack" \
| grep -Ei 'www-authenticate|^server|^HTTP'
done
</code></pre>
<p>Expect: the first two <code>curl</code>s show the identity substitution and the <code>add_repo</code> message; the loop shows <code>realm="ccr-gitengine"</code> for the <code>ghs_</code> case only, and <code>realm="GitHub"</code> with <code>Server: GitHub-Babel/3.0</code> for every other string, garbage included.</p>
<hr>
<p><em>No real token is reproduced anywhere in this report.</em></p></body></html>