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

  1. Inspect the exact path with `ls -la <exact>` and `readlink <exact>`.
  2. Replace the symlink/non-regular entry with the real prompt file content.
  3. Ensure prompt data files are committed as real files, not symlinks, in the repo.
  4. 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

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


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