CherryHQ/cherry-studio · error · Error

Agent memory directory must be a real directory: ${memoryPat

Error message

Agent memory directory must be a real directory: ${memoryPath}

What it means

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.

Source

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

  } 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
}

export async function removeAgentDataDirectory(agentsDataRoot: string, agentId: string): Promise<void> {
  const agentDataPath = agentDataDirectoryPath(agentsDataRoot, agentId)
  await assertAgentStoragePath(agentsDataRoot, agentDataPath)
  const targetStat = await lstatIfExists(agentDataPath)
  if (!targetStat) return
  if (!targetStat.isDirectory || targetStat.isSymbolicLink) {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Inspect: `ls -la <agentDataPath>/memory` and `readlink` to see the entry type.
  2. If it is a stray file/symlink, remove it so ensureAgentDataDirectory can recreate the directory.
  3. Move the agents data root off cloud-synced storage.
  4. Re-run ensureAgentDataDirectory to rebuild the memory directory.

Example fix

// before: memory entry is a file, not a directory
appData/agents/<id>/memory   (regular file)

// after: real directory
rm appData/agents/<id>/memory
await ensureAgentDataDirectory(root, id)  // recreates the memory/ dir
Defensive patterns

Strategy: try-catch

Validate before calling

import { lstat } from 'node:fs/promises'
import path from 'node:path'
async function memoryDirIsReal(agentDataPath: string): Promise<boolean> {
  try {
    const s = await lstat(path.join(agentDataPath, 'memory'))
    return s.isDirectory() && !s.isSymbolicLink()
  } catch {
    return false
  }
}
if (!(await memoryDirIsReal(agentDataPath))) {
  await ensureAgentDataDirectory(root, agentId) // recreate memory dir
}

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 && /memory directory must be a real directory/.test(e.message)) {
    await ensureAgentDataDirectory(root, agentId) // attempt repair
  } else throw e
}

Prevention

When it happens

Trigger: assertAgentDataDirectory runs against an agent whose {agentDataPath}/memory entry is a regular file, symlink, or other non-directory filesystem entry.

Common situations: 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.

Related errors


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