stablyai/orca · error · Error
Access denied: worktree does not belong to repository
Error message
Access denied: worktree does not belong to repository
What it means
Thrown by the hosted-review IPC handler when a remote (SSH/connectionId) repository receives a worktreePath that does not match any worktree returned by listRepoWorktrees after posix normalization. It is an authorization guard: the caller is asking to operate on a worktree the repository does not actually own. The path comparison uses normalizeRemoteHostedReviewPath on both sides so trailing slashes and relative segments are canonicalized before matching.
Source
Thrown at src/main/ipc/hosted-review.ts:55
}
async function resolveHostedReviewWorktreePath(
repo: Repo,
store: Store,
worktreePath?: string
): Promise<string> {
if (!worktreePath) {
return repo.path
}
if (repo.connectionId) {
const remoteWorktreePath = normalizeRemoteHostedReviewPath(worktreePath)
const repoWorktrees = await listRepoWorktrees(repo)
if (
!repoWorktrees.some(
(worktree) => normalizeRemoteHostedReviewPath(worktree.path) === remoteWorktreePath
)
) {
throw new Error('Access denied: worktree does not belong to repository')
}
return remoteWorktreePath
}
const resolvedWorktreePath = await resolveRegisteredWorktreePath(worktreePath, store)
const localGitOptions = getLocalProjectWorktreeGitOptions(store, repo)
const repoWorktrees =
Object.keys(localGitOptions).length > 0
? await listRepoWorktrees(repo, localGitOptions)
: await listRepoWorktrees(repo)
if (!repoWorktrees.some((worktree) => resolve(worktree.path) === resolvedWorktreePath)) {
throw new Error('Access denied: worktree does not belong to repository')
}
return resolvedWorktreePath
}
function normalizeRemoteHostedReviewPath(remotePath: string): string {
if (!remotePath || remotePath.includes('\0')) {
throw new Error('Access denied: invalid worktree path')View on GitHub (pinned to 1136503c6a)
Solutions
- Verify the worktreePath you pass is one of the exact paths returned by the worktree-listing IPC for that repo before invoking the hosted-review call.
- If the worktree was removed on the remote, refresh/purge the renderer's cached worktree list and reselect a current one.
- Ensure the path is a POSIX-style absolute path when repo.connectionId is set; normalizeRemoteHostedReviewPath uses posix.normalize, not local path.resolve.
- Confirm the repo connectionId maps to the same remote host that owns the worktree.
Example fix
// before
await ipc.call('hostedReview:forBranch', { repoPath, worktreePath: staleCachedPath })
// after
const worktrees = await ipc.call('worktree:list', { repoPath })
const match = worktrees.find(w => normalizeRemoteHostedReviewPath(w.path) === normalizeRemoteHostedReviewPath(worktreePath))
if (!match) throw new Error('worktree no longer registered')
await ipc.call('hostedReview:forBranch', { repoPath, worktreePath: match.path }) Defensive patterns
Strategy: validation
Validate before calling
const worktrees = await ipc.call('worktree:list', { repoPath })
const normalized = normalizeRemoteHostedReviewPath(worktreePath)
if (!worktrees.some(w => normalizeRemoteHostedReviewPath(w.path) === normalized)) {
throw new Error('worktree not registered to this repo')
} Type guard
function isRegisteredRemoteWorktree(path: string, worktrees: { path: string }[]): boolean {
const target = posix.normalize(path).replace(/\/+$/, '')
return worktrees.some(w => posix.normalize(w.path).replace(/\/+$/, '') === target)
} Prevention
- Always source worktreePath from a fresh worktree list for the same repo, never from cached/stale state.
- When repo.connectionId is set, use POSIX absolute paths matching the remote host.
- Invalidate cached worktree paths when the worktree list changes.
When it happens
Trigger: Calling a hosted-review IPC method with a repo that has connectionId set, passing a worktreePath that is not among the paths returned by listRepoWorktrees(repo). Mismatches include wrong absolute path, a worktree registered under a different repo, a stale path from a removed worktree, or a path that resolves differently on the remote POSIX host.
Common situations: The renderer caches an old worktree path after the worktree was deleted or moved on the remote host. A user switches repos in the UI but the previously selected worktree path is still sent. Cross-platform path separators (backslash from a Windows client sent to a POSIX SSH host) cause the normalized forms to disagree. Typos or truncated paths in serialized state.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied: invalid worktree path
- Merge request fetch remote must not start with "-".
- Access denied: path resolves outside allowed directories. If
- Access denied: unknown repository or worktree path
- File changed during upload staging: '${displayPath}'
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/3159b3bbd042ab61.
Report an issue: GitHub.