CherryHQ/cherry-studio · error · Error

Access denied: Path is outside the configured workspace root

Error message

Access denied: Path is outside the configured workspace root: ${requestedPath}

What it means

Thrown by validatePath (types.ts:85) when, after expanding ~, resolving to absolute, and running realpath/nearest-existing-ancestor resolution on both the root and the target, isPathWithinRoot returns false. This is the security boundary of the filesystem MCP server: it prevents path traversal (../), symlink escapes, and absolute-path hijacks from reaching read/write/edit/grep tools. The original requestedPath (pre-resolution) is included in the message.

Source

Thrown at src/main/ai/mcp/servers/filesystem/types.ts:94

  if (normalizedTargetPath === normalizedRootPath) {
    return true
  }

  const relativePath = path.relative(normalizedRootPath, normalizedTargetPath)
  return relativePath !== '' && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)
}

// Security validation
export async function validatePath(requestedPath: string, baseDir?: string): Promise<string> {
  const expandedPath = expandHome(requestedPath)
  const root = expandHome(baseDir ?? process.cwd())
  const absolute = path.isAbsolute(expandedPath) ? path.resolve(expandedPath) : path.resolve(root, expandedPath)

  const resolvedRoot = await resolveRealOrNearestExistingPath(path.resolve(root))
  const resolvedPath = await resolveRealOrNearestExistingPath(absolute)

  if (!isPathWithinRoot(resolvedPath, resolvedRoot)) {
    throw new Error(`Access denied: Path is outside the configured workspace root: ${requestedPath}`)
  }

  return resolvedPath
}

// ============================================================================
// Edit Tool Utilities - Fuzzy matching replacers from opencode
// ============================================================================

export type Replacer = (content: string, find: string) => Generator<string, void, unknown>

// Similarity thresholds for block anchor fallback matching
const SINGLE_CANDIDATE_SIMILARITY_THRESHOLD = 0.0
const MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD = 0.3

/**
 * Levenshtein distance algorithm implementation
 */

View on GitHub (pinned to 726446b54c)

Solutions

  1. Ensure file_path resolves under the same baseDir passed to the tool; strip ../ sequences or reject them upstream.
  2. Pass the correct baseDir explicitly rather than relying on process.cwd().
  3. Resolve symlinks on the caller side and confirm the realpath is still within the workspace root.
  4. On Windows, keep paths on the same drive as baseDir.

Example fix

// before
await handleReadTool({ file_path: '/etc/passwd' }, '/workspace') // throws: Access denied
await handleReadTool({ file_path: '../../../secret' }, '/workspace') // throws: Access denied

// after
await handleReadTool({ file_path: 'src/config.json' }, '/workspace')
Defensive patterns

Strategy: validation

Validate before calling

import { validatePath } from '../types'
// Pre-flight: throws the same error before the tool does any work
const resolved = await validatePath(requestedPath, baseDir)
// or, lightweight local check:
function withinRoot(p: string, root: string): boolean {
  const rel = path.relative(path.resolve(root), path.resolve(root, p))
  return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))
}

Try / catch

try {
  await handleReadTool(args, baseDir)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Access denied')) {
    // reject the user input; do not retry with a different escape attempt
  } else throw e
}

Prevention

When it happens

Trigger: Passing an absolute path outside baseDir; using ../ to climb above the workspace root; a symlink inside the workspace whose realpath target is outside the root; on Windows, a different-drive path (C:\ vs D:\) whose relative path becomes absolute; baseDir undefined causing resolution against process.cwd() that the caller did not intend.

Common situations: Caller builds a path from user input without confining it; symlink in node_modules pointing outside the project; baseDir not passed (defaults to cwd) when the server is launched from an unexpected working directory; home-dir expansion (~) resolving outside the configured root.

Understand the failure class

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/58bf0f474f28d636. Report an issue: GitHub.