{"record":{"id":"126580893b59c559","repo":"CherryHQ/cherry-studio","slug":"not-a-regular-file-displaypath","errorCode":null,"errorMessage":"Not a regular file: ${displayPath}","messagePattern":"Not a regular file: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/security/localFileResolver.ts","lineNumber":41,"sourceCode":" */\nexport async function readCanonicalLocalFile(\n  requestedPath: string,\n  canonicalPath: string,\n  displayPath: string\n): Promise<FileAttachment> {\n  const target = AbsoluteFilePathSchema.parse(canonicalPath)\n\n  let stats: Awaited<ReturnType<typeof lstat>>\n  try {\n    stats = await lstat(target)\n  } catch (error) {\n    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {\n      throw new Error(`File not found: ${displayPath}`)\n    }\n    throw error\n  }\n  if (!stats.isFile) {\n    throw new Error(`Not a regular file: ${displayPath}`)\n  }\n\n  // The snapshot pins the inode and fixes the read length at open time, so the size\n  // check and the read see the same file even if the path is replaced meanwhile.\n  const snapshot = await openReadableFileSnapshot(target)\n  try {\n    if (snapshot.size > MAX_FILE_SIZE_BYTES) {\n      throw new Error(`File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${snapshot.size} bytes): ${displayPath}`)\n    }\n\n    const data = await readStreamToBuffer(snapshot.createReadStream())\n    // Re-check against the actual read size: the file can grow between stat and read.\n    if (data.length > MAX_FILE_SIZE_BYTES) {\n      throw new Error(`File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${data.length} bytes): ${displayPath}`)\n    }\n    const filename = path.basename(requestedPath)\n    return {\n      filename,","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/security/localFileResolver.ts#L23-L59","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { stat } from '@main/utils/file'\n\n// Pre-check that the path is a regular file before resolving\nconst s = await stat(AbsoluteFilePathSchema.parse(canonicalPath))\nif (!s.isFile) {\n  throw new Error(`Path is not a regular file — cannot attach: ${displayPath}`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await readCanonicalLocalFile(requestedPath, canonicalPath, displayPath)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Not a regular file:')) {\n    logger.warn('Attempted to attach a non-file path', { displayPath })\n    return null\n  }\n  throw error\n}","preventionTips":["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."],"tags":["filesystem","validation","not-a-file","channels"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}