CherryHQ/cherry-studio · error · Error
Not a regular file: ${displayPath}
Error message
Not a regular file: ${displayPath} What it means
Thrown by readCanonicalLocalFile() when lstat reports the path is not a regular file. The custom lstat from @main/utils/file returns isFile as a boolean (not a method), so the check !stats.isFile fires for directories, symbolic links, device files, sockets, FIFOs, and any other non-regular file type. This prevents attempting to read a directory or special file as a data buffer.
Source
Thrown at src/main/ai/channels/security/localFileResolver.ts:41
*/
export async function readCanonicalLocalFile(
requestedPath: string,
canonicalPath: string,
displayPath: string
): Promise<FileAttachment> {
const target = AbsoluteFilePathSchema.parse(canonicalPath)
let stats: Awaited<ReturnType<typeof lstat>>
try {
stats = await lstat(target)
} catch (error) {
if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
throw new Error(`File not found: ${displayPath}`)
}
throw error
}
if (!stats.isFile) {
throw new Error(`Not a regular file: ${displayPath}`)
}
// The snapshot pins the inode and fixes the read length at open time, so the size
// check and the read see the same file even if the path is replaced meanwhile.
const snapshot = await openReadableFileSnapshot(target)
try {
if (snapshot.size > MAX_FILE_SIZE_BYTES) {
throw new Error(`File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${snapshot.size} bytes): ${displayPath}`)
}
const data = await readStreamToBuffer(snapshot.createReadStream())
// Re-check against the actual read size: the file can grow between stat and read.
if (data.length > MAX_FILE_SIZE_BYTES) {
throw new Error(`File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${data.length} bytes): ${displayPath}`)
}
const filename = path.basename(requestedPath)
return {
filename,View on GitHub (pinned to 726446b54c)
Solutions
- Check that the path points to a regular file, not a directory — re-list the parent directory to see what files vs. directories exist.
- If the intent is to read directory contents, use the directory-listing API (listDirectory via @main/services/file/tree) instead of the file-read path.
- Filter workspace listings to exclude directories before presenting file choices to the agent.
Defensive patterns
Strategy: validation
Validate before calling
import { stat } from '@main/utils/file'
// Pre-check that the path is a regular file before resolving
const s = await stat(AbsoluteFilePathSchema.parse(canonicalPath))
if (!s.isFile) {
throw new Error(`Path is not a regular file — cannot attach: ${displayPath}`)
} Try / catch
try {
return await readCanonicalLocalFile(requestedPath, canonicalPath, displayPath)
} catch (error) {
if (error instanceof Error && error.message.startsWith('Not a regular file:')) {
logger.warn('Attempted to attach a non-file path', { displayPath })
return null
}
throw error
} Prevention
- Filter workspace listings to show only regular files, not directories, when presenting file choices.
- Validate file type before attempting to attach — check stats.isFile or stats.isDirectory.
- When an agent picks a path, cross-reference it against the directory listing to confirm it is a file.
When it happens
Trigger: Called after a successful lstat in readCanonicalLocalFile(). Triggers when the resolved path points to a directory, a broken symlink (though that would typically fail lstat with ENOENT), a named pipe, a device node, or a socket. The agent or user supplied a path that is valid but not a readable data file.
Common situations: An agent tried to attach a directory path as a file; the path points to a symlink whose target is a directory; a special file like /dev/null or a FIFO was specified; the agent confused a directory name with a file name from a workspace listing.
Related errors
- File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${snapsh
- File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${data.l
- Agent storage path escapes its root: ${target}
- Agent storage root must be a real directory: ${root}
- Agent storage path parent is not a directory: ${current}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/126580893b59c559.
Report an issue: GitHub.