{"record":{"id":"a57e3f2986493262","repo":"CherryHQ/cherry-studio","slug":"agent-storage-root-must-be-a-real-directory-roo","errorCode":null,"errorMessage":"Agent storage root must be a real directory: ${root}","messagePattern":"Agent storage root must be a real directory: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/main/ai/agents/agentDataDirectory.ts","lineNumber":54,"sourceCode":"      }\n    }\n  }\n}\n\n/**\n * Validate a path in Data/Agents without following symbolic links in the\n * managed root or any path component below it.\n */\nexport async function assertAgentStoragePath(agentsDataRoot: string, targetPath: string): Promise<void> {\n  const root = asAbsolutePath(path.resolve(agentsDataRoot))\n  const target = asAbsolutePath(path.resolve(targetPath))\n  if (target !== root && !isPathInside(target, root)) {\n    throw new Error(`Agent storage path escapes its root: ${target}`)\n  }\n\n  const rootStat = await lstatIfExists(root)\n  if (!rootStat?.isDirectory || rootStat.isSymbolicLink) {\n    throw new Error(`Agent storage root must be a real directory: ${root}`)\n  }\n\n  let current = root\n  const relative = path.relative(root, target)\n  for (const segment of relative ? relative.split(path.sep) : []) {\n    current = asAbsolutePath(path.join(current, segment))\n    const currentStat = await lstatIfExists(current)\n    if (!currentStat) break\n    if (currentStat.isSymbolicLink) {\n      throw new Error(`Agent storage path contains a symbolic link: ${current}`)\n    }\n    if (current !== target && !currentStat.isDirectory) {\n      throw new Error(`Agent storage path parent is not a directory: ${current}`)\n    }\n  }\n\n  const [realRoot, realTarget] = await Promise.all([\n    resolveRealOrNearestExistingPath(root),","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/agents/agentDataDirectory.ts#L36-L72","documentation":"Thrown by assertAgentStoragePath when the agents data root itself is missing, not a directory, or is a symbolic link. The check uses lstat (does not follow symlinks) and requires rootStat.isDirectory truthy and isSymbolicLink false. This ensures the managed root is a real directory before any segment walking begins, preventing a symlinked root from redirecting all operations.","triggerScenarios":"The Data/Agents root does not exist yet (not created); it exists but is a file rather than a directory; it is a symbolic link to another location. Calling assertAgentStoragePath before ensureAgentStorageDirectory has created the root would also surface this.","commonSituations":"First run before the directory is initialized; a user or another process replaced the directory with a symlink or a file; a corrupted/Partially-set-up data folder; calling the assert directly instead of via ensureAgentStorageDirectory.","solutions":["Create the root as a real directory first via ensureAgentStorageDirectory (which calls ensureDir then asserts).","If the root is a symlink or file, remove/replace it with a real directory — do not weaken the check.","Ensure no external process (sync, backup, migration tool) replaces the Data/Agents directory with a symlink.","Verify the application path configuration resolves agentsDataRoot to the intended on-disk directory."],"exampleFix":"// before (asserting before the root exists)\nawait assertAgentStoragePath(agentsDataRoot, target) // root missing -> throws\n\n// after\nawait ensureAgentStorageDirectory(agentsDataRoot, target) // creates + asserts","handlingStrategy":"validation","validationCode":"import { lstatIfExists } from './agentDataDirectory' // or inline lstat\nconst stat = await lstatIfExists(path.resolve(agentsDataRoot))\nif (!stat?.isDirectory || stat.isSymbolicLink) {\n  throw new Error(`Agents root '${agentsDataRoot}' must be a real directory`)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create the agents root via ensureAgentStorageDirectory before asserting against it.","Never allow the Data/Agents root to be a symlink or a file.","Audit that no sync/backup tool replaces the root directory with a link."],"tags":["security","filesystem","symlink","agent","validation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}