stablyai/orca · error · Error

Access denied: invalid worktree path

Error message

Access denied: invalid worktree path

What it means

Input-validation guard at the top of `resolveRegisteredWorktreePath`. It rejects a worktree path that is empty or contains a NUL byte before any filesystem call. The early rejection prevents probing the filesystem via `realpath` with maliciously crafted paths (NUL injection / empty-path behavior).

Source

Thrown at src/main/ipc/filesystem-auth.ts:412

  // Why: linked worktrees are already git-trusted; reuse the cached root index so reads don't spawn `git worktree list` each time.
  return (
    isRegisteredWorktreePath(targetPath) ||
    (await isPathAllowedByCanonicalRegisteredRoot(targetPath, options.canonicalSourcePath))
  )
}

/**
 * Resolve and verify that a worktree path belongs to a registered repo.
 *
 * Why not resolveAuthorizedPath: linked worktrees can live outside repo/workspace roots; git trusts exact `git worktree list` registration, not containment.
 */
export async function resolveRegisteredWorktreePath(
  worktreePath: string,
  store: Store
): Promise<string> {
  // Reject malformed paths (null byte) early to prevent probing via realpath.
  if (!worktreePath || worktreePath.includes('\0')) {
    throw new Error('Access denied: invalid worktree path')
  }

  const resolvedTarget = resolve(worktreePath)
  if (registeredWorktreeRoots.has(resolvedTarget) || isRepoRoot(store.getRepos(), resolvedTarget)) {
    return resolvedTarget
  }

  if (registeredWorktreeRootsDirty) {
    await ensureAuthorizedRootsCache(store)
  }

  if (registeredWorktreeRoots.has(resolvedTarget)) {
    return resolvedTarget
  }

  // Resolve symlinks only after the cheap registered-root check: on macOS realpath() can trigger TCC prompts.
  const normalizedTarget = await normalizeExistingPath(resolvedTarget)
  if (registeredWorktreeRoots.has(normalizedTarget)) {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Validate the worktree path is a non-empty string with no NUL bytes before invoking the IPC handler.
  2. Ensure callers always derive the worktree path from a known repo/worktree registration rather than user-typed input.
  3. Return a typed error to the renderer when the path is missing instead of forwarding it to main.

Example fix

// before
await ipcRenderer.invoke('fs:worktreeResolve', maybeUndefinedPath)

// after
if (!maybeUndefinedPath || maybeUndefinedPath.includes('\0')) {
  throw new Error('worktree path is required')
}
await ipcRenderer.invoke('fs:worktreeResolve', maybeUndefinedPath)
Defensive patterns

Strategy: validation

Validate before calling

// Reject empty/NUL paths at the renderer before they reach main.
function assertWorktreePath(p: unknown): asserts p is string {
  if (typeof p !== 'string' || p.length === 0 || p.includes('\0')) {
    throw new Error('worktree path is required and must not contain NUL bytes')
  }
}
assertWorktreePath(worktreePath)
await ipcRenderer.invoke('fs:worktreeResolve', worktreePath)

Type guard

function isValidWorktreePath(p: unknown): p is string {
  return typeof p === 'string' && p.length > 0 && !p.includes('\0')
}

Try / catch

try {
  await resolveRegisteredWorktreePath(worktreePath, store)
} catch (e) {
  if (e instanceof Error && e.message === 'Access denied: invalid worktree path') {
    // caller passed empty/NUL input; fix the source of the path
    throw new InvalidInputError(e.message)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling `resolveRegisteredWorktreePath(worktreePath, store)` with `worktreePath` equal to `''`, `undefined`-coerced-to-empty, or any string containing `\0`.

Common situations: Renderer sent an empty worktree path (uninitialized field); a NUL byte injected via a malformed message or tampered input; a code path that forgot to default the worktree path before calling.

Understand the failure class

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/f454186879265101. Report an issue: GitHub.