stablyai/orca · critical

Access denied: path resolves outside allowed directories. If

Error message

Access denied: path resolves outside allowed directories. If this blocks a legitimate workflow, please file a GitHub issue.

What it means

Primary filesystem-auth boundary in `resolveAuthorizedPath` (default, non-symlink-preserving branch): after `resolve(targetPath)`, the resolved target is not inside any allowed directory, registered worktree, or canonical allowed/registered root (checked via `isPathAllowedIncludingRegisteredWorktrees`). The message includes guidance to file an issue because the check is deliberately conservative.

Source

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

    error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'
  )
}

export type ResolveAuthorizedPathOptions = {
  /**
   * Canonicalize the parent but preserve the leaf so delete/rename target the symlink itself, not its destination (which may live outside allowed roots).
   */
  preserveSymlink?: boolean
}

export async function resolveAuthorizedPath(
  targetPath: string,
  store: Store,
  options: ResolveAuthorizedPathOptions = {}
): Promise<string> {
  const resolvedTarget = resolve(targetPath)
  if (!(await isPathAllowedIncludingRegisteredWorktrees(resolvedTarget, store))) {
    throw new Error(PATH_ACCESS_DENIED_MESSAGE)
  }

  if (options.preserveSymlink) {
    // Canonicalize the parent so ancestor symlinks can't redirect outside allowed roots, but keep the leaf so delete/rename act on the link itself.
    let realParent: string
    try {
      realParent = await realpath(dirname(resolvedTarget))
    } catch (error) {
      if (isENOENT(error)) {
        return resolveAuthorizedMissingPath(resolvedTarget, store)
      }
      throw error
    }
    const candidateTarget = resolve(realParent, basename(resolvedTarget))
    if (
      !(await isPathAllowedIncludingRegisteredWorktrees(candidateTarget, store, {
        canonicalSourcePath: resolvedTarget
      }))

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Confirm the path is inside a registered repo/folder-workspace root or a registered git worktree.
  2. If the path is legitimately external and user-authorized, call `authorizeExternalPath(path)` before resolving.
  3. Call `invalidateAuthorizedRootsCache()` after repo/worktree registration changes, then retry.

Example fix

// before
const p = await resolveAuthorizedPath('/etc/hosts', store)

// after
const p = await resolveAuthorizedPath(join(repoPath, 'README.md'), store)
// or, for a legitimately external user-chosen path:
authorizeExternalPath(userChosenExternalPath)
const p = await resolveAuthorizedPath(userChosenExternalPath, store)
Defensive patterns

Strategy: validation

Validate before calling

// Before resolving, confirm the path is inside an allowed root or pre-authorize it.
import { isPathInsideOrEqual } from '../../shared/cross-platform-path'
const resolved = resolve(targetPath)
const allowedRoots = getAllowedRoots(store) // repo/folder-workspace/project-group roots
if (!allowedRoots.some((root) => isPathInsideOrEqual(resolved, root))) {
  authorizeExternalPath(resolved) // only if the user explicitly approved this external path
}
await resolveAuthorizedPath(targetPath, store)

Type guard

function isInsideAnyRoot(p: string, roots: string[]): boolean {
  return roots.some((root) => isPathInsideOrEqual(p, root))
}

Try / catch

try {
  return await resolveAuthorizedPath(targetPath, store)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Access denied: path resolves outside')) {
    // surface to user; only authorize external path on explicit consent
    throw new PermissionDeniedError(e.message)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling `resolveAuthorizedPath(path, store)` with a path whose resolved form is outside every repo root, folder-workspace root, project-group subtree, registered worktree, and externally-authorized path.

Common situations: Renderer passes an absolute path from outside the workspace (e.g. `/etc/hosts`); a symlink inside the allowed root points outside it; path computed against the wrong workspace; a newly added repo whose roots cache has not been refreshed.

Understand the failure class

Related errors


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