/ultraplan remote session hallucinates a nonexistent add_repo tool and has no self-knowledge of its own repo/GitHub-access model
Summary
/ultraplan, launched from the CLI inside a local repo, spins up a remote Claude Code on the web session that has no access to that repo or its GitHub issues — even though it was invoked from within the repo's own directory with an instruction referencing a specific GitHub issue. Instead of surfacing that limitation up front, the remote session:
- Hallucinated and called a nonexistent
add_repotool, then reported itspermission_deniedresponse as if it were a real API result. - Burned the entire session diagnosing its own plumbing (bundle vs. source, account-linking vs. session-linking) instead of doing any planning work.
- Had no self-knowledge of
/ultraplan's own access model — when asked directly "how do I fix this," it could only speculate ("likely a settings/auth step... I don't recognize 'ultraplan' as a built-in feature").
The user had to leave the /ultraplan session, ask a separate Claude Code CLI conversation how the feature works, get an answer, and come back — for a feature that's supposed to save round-trips.
Steps to reproduce
- In a local repo with a GitHub remote (
gh auth statusalready authenticated locally), run:
````
/ultraplan and use the draft plan in the comment to issue #1 to get started
- The remote session attempts to read the referenced issue/comment and has no repo access.
Expected behavior
Either:
- The session is created with the current repo already attached as a source (since it was launched from inside that repo's working directory via the CLI), and can read the issue/comment directly, or
- If that's not architecturally possible, the session immediately and clearly explains it has no repo access and what the one-time fix is (link GitHub connector + select repo as source), instead of attempting a nonexistent tool call and spending the session diagnosing itself.
Actual behavior (transcript excerpts, full session linked below)
The remote agent called a tool that doesn't exist:
"No, I don't have access. Theadd_repotool returnedpermission_denied: link your GitHub account to let Claude access repositories..."
Then later admitted the tool call was fabricated:
"I need to correct something: my earlier add_repo call was a guess at a tool name that doesn't actually exist in this session — I shouldn't have invoked it as if it were real."
And explained the actual constraint only after several back-and-forth turns:
"This session was seeded from a local bundle (/home/user/.seed.bundle), not from a GitHub source, so there's no git remote and no credentials... regardless of your account being linked now. Linking your GitHub account at the claude.ai level is a prerequisite, but it only takes effect for sessions started with that repo selected as a source."
The session was eventually manually killed with no plan produced and no useful work done.
Impact
For a repo-scoped planning feature, having no repo access by default (with no upfront warning) means the first real usage attempt is a dead end, and diagnosing why requires either deep familiarity with Claude Code's session/source architecture or asking a different Claude session — undermining the point of the feature.
Suggested fix
- At minimum: detect the no-repo-access case immediately at session start and state it plainly, with the concrete one-time fix (Settings → Connectors → GitHub → grant repo access), instead of guessing at tool names.
- Ideally: a CLI-launched
/ultraplanshould attach the current repo as a source automatically (it already has local git context), so this doesn't require separate web-side configuration for the common case.
Evidence
Full session transcript: https://claude.ai/code/session_01ED5zGhstkZ7ac3AkVW1QWN?from=cli
(Note: this links to a private session tied to my account — happy to paste the raw transcript text instead if that's more useful for triage.)
3 Comments
Follow-up: dead sessions get silently reused when the prompt text matches
Re-ran
/ultraplantoday with the exact same prompt text used in the original failed session ("...use the draft plan in the comment to issue #1 to get started"). That session had already been explicitly killed (see the transcript in the original report — ended with "kk. please kill this session").Instead of starting a new session, the CLI returned:
This is the identical session ID from the original dead session, a day later. It looks like
/ultraplanroutes/dedupes by matching the prompt text (or some hash of it) to an existing session, without checking whether that session is actually still alive — so a killed session gets silently treated as "already polling" and reused, rather than the request producing a fresh session.Suggested behavior: when a new
/ultraplaninvocation matches an existing session that has already ended (killed/completed/errored), don't silently reattach to it — prompt the user with something like "This looks like a session that already ended. Recover it, or start a new one?" Silently pointing back to a dead session under an "already polling" message is misleading, since polling implies it's still active.Correction: the actual mechanism (per the docs), and a real fix suggestion
After reading
/docs/en/ultraplan, my earlier "prompt-text dedup" theory in the previous comment was likely wrong. The docs describe something different:This implies the CLI tracks a single ultraplan indicator per terminal session (shown as
◇ ultraplan/◆ ultraplan readyin the prompt bar), and that indicator is only cleared by explicitly stopping it through/tasks→ Stop ultraplan.In both of our repro attempts, we ended the session by deleting/killing it directly from the web UI instead of using
/tasks→ Stop ultraplan from the terminal. That appears to leave the CLI-side indicator stuck in a "still polling" state indefinitely — so every subsequent/ultraplaninvocation (regardless of prompt text) just reports back the same stale "already polling" session, rather than checking whether that session is actually still alive and starting fresh if not.This also plausibly explains why the local plan-mode approval dialog didn't offer the documented "No, refine with Ultraplan on Claude Code on the web" option on a subsequent local plan in the same terminal session — the CLI likely suppresses offering a new ultraplan launch while it believes one is already active.
Suggested fix, refined from the original report:
/tasks→ Stop ultraplan. Two different "end this session" affordances that leave the client in inconsistent states is the core bug.Further evidence: this looks server-side, not a local client cache issue
Dug into local state on disk to test the previous theory. Findings:
~/.claude/projects/<project>/<session>/remote-agents/*.meta.json) is for the first failed attempt (taskId: r1lt145ev,sessionId: session_01Hqg4GXRVyPnBeKCbXieHgL, the one that timed out after 90 minutes).session_01ED5zGhstkZ7ac3AkVW1QWN— the session every subsequent/ultraplaninvocation gets stuck pointing back to./tasksreports nothing running in the background — it doesn't even list this session as a trackable entry to stop.session_01ED5zGhstkZ7ac3AkVW1QWNfrom the web UI. After choosing "delete," the session now shows as archived, not gone — so it seems "delete" from the web UI may just archive under the hood rather than fully terminating anything./ultraplaninvocation (with varied prompt text, ruling out prompt-based dedup) still returns:>
ultraplan: already polling. Open https://claude.ai/code/session_01ED5zGhstkZ7ac3AkVW1QWN?from=cli...Since there's no local file tracking this session and
/tasksdoesn't see it, whatever is producing "already polling" must be checking server-side state (possibly keyed on repo/account) that isn't affected by archiving or "deleting" from the web UI, and isn't reachable via the documented/tasks→ "Stop ultraplan" flow since that flow doesn't even list it.At this point we don't have a client-side workaround — this looks like it needs someone with server-side visibility into task state to unstick it (or to confirm archiving/deleting should actually clear it, which it currently doesn't).