CherryHQ/cherry-studio · error · Error

File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${snapsh

Error message

File exceeds the ${MAX_FILE_SIZE_BYTES} byte limit (${snapshot.size} bytes): ${displayPath}

What it means

Thrown by readCanonicalLocalFile() as the first of two size checks. The snapshot (from openReadableFileSnapshot) pins the inode and records the file size at open time. If that size exceeds MAX_FILE_SIZE_BYTES (100 MB = 104857600 bytes), the error fires before any data is read. This is the fast-path rejection: it avoids reading the entire file into memory before discovering it is too large.

Source

Thrown at src/main/ai/channels/security/localFileResolver.ts:49

  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,
      data: data.toString('base64'),
      media_type: mimeForFilename(filename),
      size: data.length
    }
  } finally {
    // Swallow close errors so they can't mask an in-flight resolution error.
    await snapshot.close().catch(() => {})
  }

View on GitHub (pinned to 726446b54c)

Solutions

  1. Reduce the file size below 100 MB before attaching — compress, truncate, or extract only the relevant portion.
  2. If a larger limit is needed, change MAX_FILE_SIZE_BYTES in @main/utils/downloadAsBase64 — but consider memory and API constraints first.
  3. For large text files, read only the relevant lines or sections instead of attaching the whole file.
Defensive patterns

Strategy: validation

Validate before calling

import { stat } from '@main/utils/file'
import { MAX_FILE_SIZE_BYTES } from '@main/utils/downloadAsBase64'

// Pre-check file size before resolving
const s = await stat(AbsoluteFilePathSchema.parse(canonicalPath))
if (s.size > MAX_FILE_SIZE_BYTES) {
  throw new Error(`File is ${s.size} bytes — exceeds the ${MAX_FILE_SIZE_BYTES} byte limit`)  
}

Try / catch

try {
  return await readCanonicalLocalFile(requestedPath, canonicalPath, displayPath)
} catch (error) {
  if (error instanceof Error && error.message.includes('byte limit')) {
    logger.warn('File too large to attach', { displayPath })
    return null
  }
  throw error
}

Prevention

When it happens

Trigger: Called from resolveWorkspaceFile or resolveLocalFile when the target file's size at open time exceeds 100 MB. The snapshot captures the size from the file descriptor's stat, which reflects the inode state at open time.

Common situations: An agent tried to attach a large binary file (video, archive, database dump) as a document; a log file grew past 100 MB; the user pointed the agent at a media file that exceeds the attachment limit.

Related errors


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