CherryHQ/cherry-studio · error · Error
Path is outside the workspace: ${userPath}
Error message
Path is outside the workspace: ${userPath} What it means
Thrown by resolveWorkspaceFile() as a security containment check. After resolving both workspaceRoot and the requested path through realpath (which follows symlinks), it verifies the target's real path starts with realRoot + path.sep. If it does not, the path — after symlink resolution — escapes the workspace boundary. This is defense-in-depth against traversal mistakes and prompt-injection path selection, not a sandbox against code-executing agents.
Source
Thrown at src/main/ai/channels/security/WorkspaceFileGuard.ts:49
// waste retries on other paths.
if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
throw new Error(`Session workspace is unavailable: ${workspaceRoot}`)
}
throw error
}
let realTarget: string
try {
realTarget = await realpath(requested)
} catch (error) {
if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
throw new Error(`File not found in workspace: ${userPath}`)
}
throw error
}
if (realTarget !== realRoot && !realTarget.startsWith(realRoot + path.sep)) {
throw new Error(`Path is outside the workspace: ${userPath}`)
}
return readCanonicalLocalFile(requested, realTarget, userPath)
}
View on GitHub (pinned to 726446b54c)
Solutions
- Use only paths relative to the workspace root without '../' sequences.
- If the path legitimately needs to be outside the workspace, use resolveLocalFile() instead (which does no containment check) — but understand this removes the security boundary.
- Audit workspace symlinks: remove or reject symlinks that point outside the workspace root.
Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path'
function isPathWithinWorkspace(workspaceRoot: string, userPath: string): boolean {
const resolved = path.resolve(workspaceRoot, userPath)
return resolved === workspaceRoot || resolved.startsWith(workspaceRoot + path.sep)
}
if (!isPathWithinWorkspace(workspaceRoot, userPath)) {
throw new Error(`Path '${userPath}' escapes the workspace — use a workspace-relative path without '../'`)
} Try / catch
try {
return await resolveWorkspaceFile(workspaceRoot, userPath)
} catch (error) {
if (error instanceof Error && error.message.startsWith('Path is outside the workspace:')) {
// Security violation — log and reject, do not retry with alternative paths
logger.warn('Path escape attempt blocked', { userPath, workspaceRoot })
throw error
}
throw error
} Prevention
- Always use paths relative to the workspace root — avoid absolute paths and '../' sequences.
- Audit workspace symlinks and remove any that point outside the workspace boundary.
- Treat this error as a security signal — investigate if it appears in agent logs, as it may indicate prompt injection.
When it happens
Trigger: Called when an agent supplies a path containing '../' sequences that resolve outside the workspace, or when a symlink inside the workspace points to a target outside the workspace. The check fires after realpath resolves everything, so even an obfuscated path like a symlink chain that eventually escapes will be caught.
Common situations: An agent following a prompt-injection instruction tried to read '../../../etc/passwd' or an absolute path outside the workspace; a symlink in the workspace points to /etc or the user's home directory; the agent was given a workspace-adjacent path that resolves to a sibling directory outside the root.
Related errors
- Agent storage path contains a symbolic link: ${current}
- Agent storage path resolves outside its root: ${target}
- Access denied: Path is outside the configured workspace root
- Agent storage path escapes its root: ${target}
- Agent storage root must be a real directory: ${root}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/eae43e674d19dbad.
Report an issue: GitHub.