CherryHQ/cherry-studio · error · Error
Required agent prompt file is not a regular file: ${exact}
Error message
Required agent prompt file is not a regular file: ${exact} What it means
Thrown by resolveFile (with failOnError=true) when an exact-match prompt file path exists but lstat reports it is not a regular file or is a symbolic link. Symlinks are explicitly ignored (logged as a warning) and, in failOnError mode, escalate to a throw. This keeps agent prompt assembly from following symlinks, which could read files outside the prompt data directory.
Source
Thrown at src/main/ai/agents/prompt.ts:22
import { loggerService } from '@logger'
import type { AgentConfiguration } from '@shared/data/types/agent'
import { buildBootstrapInstructions, SOUL_CONTENT_THRESHOLD } from './bootstrap'
const logger = loggerService.withContext('PromptBuilder')
/**
* Resolve a filename within a directory using case-insensitive matching.
* Returns the full path if found (preferring exact match), or undefined.
*/
async function resolveFile(dir: string, name: string, failOnError = false): Promise<string | undefined> {
const exact = path.join(dir, name)
try {
const fileStat = await lstat(exact)
if (fileStat.isFile() && !fileStat.isSymbolicLink()) return exact
if (fileStat.isSymbolicLink()) logger.warn('Ignoring symbolic link in agent prompt data', { path: exact })
if (failOnError) throw new Error(`Required agent prompt file is not a regular file: ${exact}`)
return undefined
} catch (error) {
if (failOnError && (error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
// 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 undefined
const matchedPath = path.join(dir, match)
const fileStat = await lstat(matchedPath)
if (fileStat.isFile() && !fileStat.isSymbolicLink()) return matchedPath
if (fileStat.isSymbolicLink()) logger.warn('Ignoring symbolic link in agent prompt data', { path: matchedPath })
if (failOnError) throw new Error(`Required agent prompt file is not a regular file: ${matchedPath}`)
return undefined
} catch (error) {View on GitHub (pinned to 726446b54c)
Solutions
- Inspect the exact path with `ls -la <exact>` and `readlink <exact>`.
- Replace the symlink/non-regular entry with the real prompt file content.
- Ensure prompt data files are committed as real files, not symlinks, in the repo.
- If the file is optional, call resolveFile without failOnError so it returns undefined instead of throwing.
Example fix
// before: required prompt file is a symlink prompts/system.md -> /etc/something // after: real file rm prompts/system.md echo "..." > prompts/system.md # a real regular file
Defensive patterns
Strategy: validation
Validate before calling
import { lstat } from 'node:fs/promises'
import path from 'node:path'
async function isRegularPromptFile(dir: string, name: string): Promise<boolean> {
try {
const s = await lstat(path.join(dir, name))
return s.isFile() && !s.isSymbolicLink()
} catch {
return false
}
}
if (!(await isRegularPromptFile(dir, name))) {
// skip failOnError; mark prompt resource as missing/unsafe
} Type guard
function isRegularFile(stat: import('node:fs').Stats): boolean {
return stat.isFile() && !stat.isSymbolicLink()
} Try / catch
try {
const resolved = await resolveFile(dir, name, true)
} catch (e) {
if (e instanceof Error && /not a regular file/.test(e.message)) {
// required prompt resource is unsafe; abort prompt assembly with a clear error
} else throw e
} Prevention
- Store prompt resources as real files, not symlinks.
- Keep prompt data directories off synced storage.
- Only set failOnError for truly required resources.
When it happens
Trigger: resolveFile(dir, name, failOnError=true) is called for a required prompt resource whose exact path exists but is a symlink, directory, FIFO, or special file.
Common situations: A required prompt file (e.g. a system-prompt fragment) was replaced by a symlink; the prompt data dir lives on synced storage that materializes placeholders; tampering; a directory created where a file was expected.
Related errors
- Required agent prompt file is not a regular file: ${matchedP
- Agent data file must be a real file: ${filePath}
- Agent data directory must be a real directory: ${agentDataPa
- Agent memory directory must be a real directory: ${memoryPat
- Refusing to recursively remove unsafe agent data path: ${age
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/5ad6453b9d15a31f.
Report an issue: GitHub.