CherryHQ/cherry-studio · error · Error
Agent memory file must be a real file: ${matchedPath}
Error message
Agent memory file must be a real file: ${matchedPath} What it means
The second branch of resolveFileCI(): when no exact-case match exists, it scans the directory case-insensitively. If a case-variant match (e.g. 'fact.md' when looking for 'FACT.md') is found but that entry is a symlink or non-regular file, this throws. Same security invariant as the exact-match branch, applied to the fallback resolution.
Source
Thrown at src/main/ai/mcp/servers/agentMemory.ts:45
const fileStat = await lstat(exact)
if (!fileStat.isFile() || fileStat.isSymbolicLink()) {
throw new Error(`Agent memory file must be a real file: ${exact}`)
}
return exact
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err
// exact match not found, try case-insensitive
}
try {
const entries = await readdir(dir)
const target = name.toLowerCase()
const match = entries.find((e) => e.toLowerCase() === target)
if (!match) return exact
const matchedPath = path.join(dir, match)
const fileStat = await lstat(matchedPath)
if (!fileStat.isFile() || fileStat.isSymbolicLink()) {
throw new Error(`Agent memory file must be a real file: ${matchedPath}`)
}
return matchedPath
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
logger.warn('Unexpected error reading directory', { dir, error: (err as Error).message })
}
return exact
}
}
type JournalEntry = {
ts: string
tags: string[]
text: string
}
const MEMORY_TOOL: Tool = {
name: 'memory',View on GitHub (pinned to 726446b54c)
Solutions
- List the memory directory and identify the case-variant entry: ls -la <agentDataPath>/memory/.
- Remove the offending case-variant symlink/non-file and create FACT.md or JOURNAL.jsonl as a regular file with the correct case.
- On case-insensitive filesystems, move the file to a temp name first to force the correct case.
Example fix
# before: fact.md (lowercase) is a symlink ls memory/ # fact.md -> /elsewhere # after rm memory/fact.md printf '# Facts ' > memory/FACT.md
Defensive patterns
Strategy: validation
Validate before calling
import { lstat, readdir } from 'node:fs/promises'
import path from 'node:path'
async function safeResolveCi(dir: string, name: string): Promise<string | null> {
const target = name.toLowerCase()
const entries = await readdir(dir).catch(() => [])
const match = entries.find((e) => e.toLowerCase() === target)
if (!match) return null
const p = path.join(dir, match)
const s = await lstat(p)
return s.isFile() && !s.isSymbolicLink() ? p : null
} Prevention
- On case-insensitive filesystems (macOS/Windows), always create memory files with the canonical case (FACT.md, JOURNAL.jsonl).
- Avoid symlinks anywhere under the agent data directory.
- When porting a workspace across OSes, normalize file casing.
When it happens
Trigger: The exact filename is absent, but a case-different variant exists and is a symlink/directory/special file. Happens on case-insensitive filesystems (macOS APFS, Windows NTFS) where the OS may report a differently-cased entry, or when a user manually created 'fact.md' as a symlink.
Common situations: macOS users whose filesystem folds case; a symlink left from a migration; a directory accidentally named with wrong case; cross-platform sync creating case variants.
Related errors
- Agent memory file must be a real file: ${exact}
- Agent memory directory must be a real directory: ${memoryDir
- Agent memory file must be a real file: ${filePath}
- Required agent prompt file is not a regular file: ${matchedP
- Agent journal must be a regular file: ${journalPath}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/8e7bb04f9ea17b7f.
Report an issue: GitHub.