CherryHQ/cherry-studio · error · Error
Cannot read binary file: ${filePath}
Error message
Cannot read binary file: ${filePath} What it means
Thrown by the read tool after isBinaryFile(validPath) returns true. isBinaryFile (types.ts:557) reads the first 4096 bytes and applies a heuristic: >5% null bytes (unless the distribution looks like UTF-16), or >30% non-printable bytes outside the ASCII/whitespace/high-byte ranges. The read tool reads with 'utf-8' encoding, so binary content would corrupt output and is rejected upfront.
Source
Thrown at src/main/ai/mcp/servers/filesystem/tools/read.ts:55
const filePath = parsed.data.file_path
const validPath = await validatePath(filePath, baseDir)
// Check if file exists
try {
const stats = await fs.stat(validPath)
if (!stats.isFile()) {
throw new Error(`Path is not a file: ${filePath}`)
}
} catch (error: any) {
if (error.code === 'ENOENT') {
throw new Error(`File not found: ${filePath}`)
}
throw error
}
// Check if file is binary
if (await isBinaryFile(validPath)) {
throw new Error(`Cannot read binary file: ${filePath}`)
}
// Read file content
const content = await fs.readFile(validPath, 'utf-8')
const lines = content.split('\n')
// Apply offset and limit
const offset = (parsed.data.offset || 1) - 1 // Convert to 0-based
const limit = parsed.data.limit || DEFAULT_READ_LIMIT
if (offset < 0 || offset >= lines.length) {
throw new Error(`Invalid offset: ${offset + 1}. File has ${lines.length} lines.`)
}
const selectedLines = lines.slice(offset, offset + limit)
// Format output with line numbers and truncate long lines
const output: string[] = []View on GitHub (pinned to 726446b54c)
Solutions
- Do not use the text read tool for binary assets; use a dedicated binary reader or stream the file directly.
- If the file is genuinely text but misdetected (e.g. UTF-16 without BOM), convert it to UTF-8 first.
- For borderline files, confirm with the `file` command or by inspecting the first bytes before calling read.
Example fix
// before
await handleReadTool({ file_path: 'assets/logo.png' }, baseDir) // throws: Cannot read binary file
// after
import fs from 'fs/promises'
const buf = await fs.readFile(path.join(baseDir, 'assets/logo.png')) // handle as binary Defensive patterns
Strategy: validation
Validate before calling
import { isBinaryFile } from '../types'
async function safeRead(p: string) {
if (await isBinaryFile(p)) throw new Error(`Refusing binary: ${p}`)
// proceed with text read
} Try / catch
try {
await handleReadTool(args, baseDir)
} catch (e) {
if (e instanceof Error && e.message.startsWith('Cannot read binary file')) {
// route to a binary handler instead
} else throw e
} Prevention
- Maintain an allowlist of text extensions (.ts/.js/.md/.json/.txt) and reject the rest before reading.
- For assets, use a dedicated binary read path rather than the text read tool.
- Convert UTF-16 or other encoded text files to UTF-8 before they enter the workspace.
When it happens
Trigger: Calling handleReadTool on an executable, image, archive, compiled object, database file, or any file whose first 4KB contains null bytes or many non-printable bytes. The path is valid, exists, and is a regular file, so all earlier checks pass.
Common situations: Reading assets (png/jpg/wasm/so/dll) that happen to live in the workspace; reading a SQLite .db or pickle file; a UTF-16 file whose zero-byte pattern does not trip the UTF-16 escape ratio but exceeds the 5% threshold; large binary blobs with no extension.
Related errors
- Path is not a file: ${filePath}
- File not found: ${filePath}
- Invalid offset: ${offset + 1}. File has ${lines.length} line
- Invalid arguments for write: ${parsed.error}
- Access denied: Path is outside the configured workspace root
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/f61451c28770991c.
Report an issue: GitHub.