CherryHQ/cherry-studio · error · Error

Agent data directory must be a real directory: ${agentDataPa

Error message

Agent data directory must be a real directory: ${agentDataPath}

What it means

Thrown by assertAgentDataDirectory while validating an agent's data store: the root agent data path exists but lstat reports it is not a directory, or it is a symbolic link. assertAgentDataDirectory is the integrity gate called by ensureAgentDataDirectory after creation, so a non-directory root means the data store is corrupt or has been tampered with.

Source

Thrown at src/main/ai/agents/agentDataDirectory.ts:167

  }

  await mkdir(asAbsolutePath(agentDataPath))
  try {
    await ensureAgentDataDirectory(agentsDataRoot, agentId)
    return agentDataPath
  } catch (error) {
    await removeAgentDataDirectory(agentsDataRoot, agentId).catch(() => undefined)
    throw error
  }
}

export async function assertAgentDataDirectory(agentsDataRoot: string, agentId: string): Promise<string> {
  const agentDataPath = agentDataDirectoryPath(agentsDataRoot, agentId)
  await assertAgentStoragePath(agentsDataRoot, agentDataPath)

  const rootStat = await lstatIfExists(agentDataPath)
  if (!rootStat?.isDirectory || rootStat.isSymbolicLink) {
    throw new Error(`Agent data directory must be a real directory: ${agentDataPath}`)
  }

  const memoryPath = path.join(agentDataPath, 'memory')
  const memoryStat = await lstatIfExists(memoryPath)
  if (!memoryStat?.isDirectory || memoryStat.isSymbolicLink) {
    throw new Error(`Agent memory directory must be a real directory: ${memoryPath}`)
  }

  for (const filename of AGENT_DATA_FILES) {
    const filePath = path.join(agentDataPath, filename)
    const fileStat = await lstatIfExists(filePath)
    if (fileStat && (!fileStat.isFile || fileStat.isSymbolicLink)) {
      throw new Error(`Agent data file must be a real file: ${filePath}`)
    }
  }
  return agentDataPath
}

View on GitHub (pinned to 726446b54c)

Solutions

  1. Inspect the path: `ls -la <agentDataPath>` to confirm whether it is a file/symlink/device.
  2. If it is not legitimate agent data, remove the offending entry and let the app recreate the directory.
  3. Restore the agent data directory from a known-good backup if real data is involved.
  4. Prevent external processes from replacing directories with symlinks/files under the data root.

Example fix

// before: agent data root path is a symlink
appData/agents/<id> -> /elsewhere

// after: real directory
rm appData/agents/<id>
// app recreates a real directory on next ensureAgentDataDirectory()
Defensive patterns

Strategy: try-catch

Validate before calling

import { lstat } from 'node:fs/promises'
async function isRealDir(p: string): Promise<boolean> {
  try {
    const s = await lstat(p)
    return s.isDirectory() && !s.isSymbolicLink()
  } catch {
    return false
  }
}
if (!(await isRealDir(agentDataPath))) {
  throw new Error(`Agent data path is not a real directory: ${agentDataPath}`)
}

Type guard

function isRealDirectory(stat: import('node:fs').Stats): boolean {
  return stat.isDirectory() && !stat.isSymbolicLink()
}

Try / catch

try {
  await assertAgentDataDirectory(root, agentId)
} catch (e) {
  if (e instanceof Error && /must be a real directory/.test(e.message)) {
    // mark agent data store corrupt; offer user a reset
  } else throw e
}

Prevention

When it happens

Trigger: Any code path that runs assertAgentDataDirectory (directly or via ensureAgentDataDirectory/createAgentDataDirectory) against an agentId whose {agentsDataRoot}/{agentId} is a regular file, symlink, or other non-directory entry.

Common situations: A symlink substituted for the agent dir (tampering or sync placeholder); a file created at the directory path by a buggy tool; restore from backup that collapsed a dir into a file; filesystem corruption.

Related errors


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