{"record":{"id":"3c69829e01bb3044","repo":"CherryHQ/cherry-studio","slug":"agent-memory-directory-must-be-a-real-directory","errorCode":null,"errorMessage":"Agent memory directory must be a real directory: ${memoryPath}","messagePattern":"Agent memory directory must be a real directory: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/agents/agentDataDirectory.ts","lineNumber":173,"sourceCode":"  } catch (error) {\n    await removeAgentDataDirectory(agentsDataRoot, agentId).catch(() => undefined)\n    throw error\n  }\n}\n\nexport async function assertAgentDataDirectory(agentsDataRoot: string, agentId: string): Promise<string> {\n  const agentDataPath = agentDataDirectoryPath(agentsDataRoot, agentId)\n  await assertAgentStoragePath(agentsDataRoot, agentDataPath)\n\n  const rootStat = await lstatIfExists(agentDataPath)\n  if (!rootStat?.isDirectory || rootStat.isSymbolicLink) {\n    throw new Error(`Agent data directory must be a real directory: ${agentDataPath}`)\n  }\n\n  const memoryPath = path.join(agentDataPath, 'memory')\n  const memoryStat = await lstatIfExists(memoryPath)\n  if (!memoryStat?.isDirectory || memoryStat.isSymbolicLink) {\n    throw new Error(`Agent memory directory must be a real directory: ${memoryPath}`)\n  }\n\n  for (const filename of AGENT_DATA_FILES) {\n    const filePath = path.join(agentDataPath, filename)\n    const fileStat = await lstatIfExists(filePath)\n    if (fileStat && (!fileStat.isFile || fileStat.isSymbolicLink)) {\n      throw new Error(`Agent data file must be a real file: ${filePath}`)\n    }\n  }\n  return agentDataPath\n}\n\nexport async function removeAgentDataDirectory(agentsDataRoot: string, agentId: string): Promise<void> {\n  const agentDataPath = agentDataDirectoryPath(agentsDataRoot, agentId)\n  await assertAgentStoragePath(agentsDataRoot, agentDataPath)\n  const targetStat = await lstatIfExists(agentDataPath)\n  if (!targetStat) return\n  if (!targetStat.isDirectory || targetStat.isSymbolicLink) {","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/agents/agentDataDirectory.ts#L155-L191","documentation":"Thrown by assertAgentDataDirectory when the per-agent `memory` subdirectory exists but is not a real directory or is a symbolic link. The memory dir holds agent memory artifacts and must be a plain directory; a symlink or file there is treated as corruption/tampering.","triggerScenarios":"assertAgentDataDirectory runs against an agent whose {agentDataPath}/memory entry is a regular file, symlink, or other non-directory filesystem entry.","commonSituations":"A sync service replaced the memory dir with a placeholder file/symlink; a tool created a file named `memory`; tampering; partial restore that did not recreate subdirectories.","solutions":["Inspect: `ls -la <agentDataPath>/memory` and `readlink` to see the entry type.","If it is a stray file/symlink, remove it so ensureAgentDataDirectory can recreate the directory.","Move the agents data root off cloud-synced storage.","Re-run ensureAgentDataDirectory to rebuild the memory directory."],"exampleFix":"// before: memory entry is a file, not a directory\nappData/agents/<id>/memory   (regular file)\n\n// after: real directory\nrm appData/agents/<id>/memory\nawait ensureAgentDataDirectory(root, id)  // recreates the memory/ dir","handlingStrategy":"try-catch","validationCode":"import { lstat } from 'node:fs/promises'\nimport path from 'node:path'\nasync function memoryDirIsReal(agentDataPath: string): Promise<boolean> {\n  try {\n    const s = await lstat(path.join(agentDataPath, 'memory'))\n    return s.isDirectory() && !s.isSymbolicLink()\n  } catch {\n    return false\n  }\n}\nif (!(await memoryDirIsReal(agentDataPath))) {\n  await ensureAgentDataDirectory(root, agentId) // recreate memory dir\n}","typeGuard":"function isRealDirectory(stat: import('node:fs').Stats): boolean {\n  return stat.isDirectory() && !stat.isSymbolicLink()\n}","tryCatchPattern":"try {\n  await assertAgentDataDirectory(root, agentId)\n} catch (e) {\n  if (e instanceof Error && /memory directory must be a real directory/.test(e.message)) {\n    await ensureAgentDataDirectory(root, agentId) // attempt repair\n  } else throw e\n}","preventionTips":["Do not let sync tools replace the memory directory with a file/symlink.","Recreate the memory dir via ensureAgentDataDirectory when missing.","Validate agent data integrity before heavy operations."],"tags":["filesystem","security","symlink","agents","memory"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}