CherryHQ/cherry-studio · critical · Error
Agent storage path resolves outside its root: ${target}
Error message
Agent storage path resolves outside its root: ${target} What it means
Thrown by assertAgentStoragePath as the final defense: after the lexical and lstat checks pass, the code resolves the real (symlink-followed) paths of both root and target via realpath and confirms the real target is still inside the real root. This catches a symlink OUTSIDE the managed tree (e.g. a parent directory above the root being a symlink) that the in-tree walk would not see, closing the gap between lexical containment and actual filesystem resolution.
Source
Thrown at src/main/ai/agents/agentDataDirectory.ts:76
const relative = path.relative(root, target)
for (const segment of relative ? relative.split(path.sep) : []) {
current = asAbsolutePath(path.join(current, segment))
const currentStat = await lstatIfExists(current)
if (!currentStat) break
if (currentStat.isSymbolicLink) {
throw new Error(`Agent storage path contains a symbolic link: ${current}`)
}
if (current !== target && !currentStat.isDirectory) {
throw new Error(`Agent storage path parent is not a directory: ${current}`)
}
}
const [realRoot, realTarget] = await Promise.all([
resolveRealOrNearestExistingPath(root),
resolveRealOrNearestExistingPath(target)
])
if (realTarget !== realRoot && !isPathInside(realTarget, realRoot)) {
throw new Error(`Agent storage path resolves outside its root: ${target}`)
}
}
/** Ensure a Data/Agents path is a real directory contained by the Agent storage root. */
export async function ensureAgentStorageDirectory(agentsDataRoot: string, targetPath: string): Promise<void> {
await ensureDir(asAbsolutePath(path.resolve(agentsDataRoot)))
await assertAgentStoragePath(agentsDataRoot, targetPath)
await ensureDir(asAbsolutePath(path.resolve(targetPath)))
await assertAgentStoragePath(agentsDataRoot, targetPath)
const targetStat = await lstat(asAbsolutePath(path.resolve(targetPath)))
if (!targetStat.isDirectory || targetStat.isSymbolicLink) {
throw new Error(`Agent storage directory must be a real directory: ${targetPath}`)
}
}
function assertAgentId(agentId: string): void {
if (!agentId || agentId === '.' || agentId === '..' || agentId.toLowerCase() === 'system' || /[\\/]/.test(agentId)) {
throw new Error(`Invalid agent id for data directory: ${agentId}`)View on GitHub (pinned to 726446b54c)
Solutions
- Resolve the agentsDataRoot to its real path at configuration time and store/use the realpath consistently.
- Remove symlinks in the parent chain of the data root, or point agentsDataRoot at a real path with no symlinked ancestors.
- Ensure the data root lives on a real directory tree not under any symlinked parent (common with cloud-synced home folders).
- If on a mapped/container volume, verify the host path the container sees matches expectations.
Defensive patterns
Strategy: validation
Validate before calling
import { realpath } from 'node:fs/promises'
import path from 'node:path'
import { isPathInside } from '@main/utils/file'
const realRoot = await realpath(path.resolve(agentsDataRoot))
const realTarget = await realpath(path.resolve(targetPath))
if (realTarget !== realRoot && !isPathInside(realTarget, realRoot)) {
throw new Error(`Refusing: real path '${realTarget}' escapes real root '${realRoot}'`)
} Prevention
- Store and use the realpath of the agents root at configuration time.
- Avoid placing the data root under symlinked parent directories (common with cloud-synced homes).
- On container/volume setups, verify the host path matches what the container resolves.
When it happens
Trigger: A symlink exists above or alongside the managed root such that, once resolved, the target's real path lies outside the real root — e.g. the root itself is reachable through a link, or an external symlink was injected that the segment walk (limited to root..target) did not traverse.
Common situations: The agentsDataRoot path itself contains a symlink component (e.g. /Users/X is a link); a symlink in a parent directory redirects resolution outside; a creative traversal that passes lexical checks but escapes on realpath; containerized/mapped volumes where paths differ from the host.
Related errors
- Agent storage path contains a symbolic link: ${current}
- Agent storage path escapes its root: ${target}
- Agent storage root must be a real directory: ${root}
- Agent storage directory must be a real directory: ${targetPa
- Invalid agent id for data directory: ${agentId}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/d81c23cb0c99ee98.
Report an issue: GitHub.