ultraworkers/claw-code · error · std::io::Error
file appears to be binary
Error message
file appears to be binary
What it means
`read_file` (runtime/src/file_ops.rs:207) calls `is_binary_file`, which reads the first 8 KiB and flags the file as binary if it contains any NUL byte (0x00). Rejection is `ErrorKind::InvalidData`. Note that UTF-16-encoded text files contain NUL high bytes and are therefore classified as binary, not just images/executables.
Source
Thrown at rust/crates/runtime/src/file_ops.rs:207
) -> io::Result<ReadFileOutput> {
let absolute_path = normalize_path(path)?;
// Check file size before reading
let metadata = fs::metadata(&absolute_path)?;
if metadata.len() > MAX_READ_SIZE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"file is too large ({} bytes, max {} bytes)",
metadata.len(),
MAX_READ_SIZE
),
));
}
// Detect binary files
if is_binary_file(&absolute_path)? {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"file appears to be binary",
));
}
let content = fs::read_to_string(&absolute_path)?;
let lines: Vec<&str> = content.lines().collect();
let start_index = offset.unwrap_or(0).min(lines.len());
let end_index = limit.map_or(lines.len(), |limit| {
start_index.saturating_add(limit).min(lines.len())
});
let selected = lines[start_index..end_index].join("\n");
Ok(ReadFileOutput {
kind: String::from("text"),
file: TextFilePayload {
file_path: absolute_path.to_string_lossy().into_owned(),
content: selected,View on GitHub (pinned to 08106b0c37)
Solutions
- If it really is text in UTF-16, convert first: `iconv -f UTF-16 -t UTF-8 file > file.u8` and read that.
- For genuine binaries, use the Bash tool (e.g. `file`, `xxd | head`, `base64`) instead of the Read tool.
- Pre-check with the same heuristic (NUL in first 8 KiB) before handing the path to read_file.
Example fix
# before Read(file="export.csv") # file appears to be binary (UTF-16 file) # after Bash(command="iconv -f UTF-16 -t UTF-8 export.csv > export.utf8.csv") Read(file="export.utf8.csv")
Defensive patterns
Strategy: validation
Validate before calling
fn looks_binary(p: &Path) -> io::Result<bool> {
use std::io::Read;
let mut f = std::fs::File::open(p)?;
let mut buf = [0u8; 8192];
let n = f.read(&mut buf)?;
Ok(buf[..n].contains(&0)) // same heuristic as is_binary_file
} Try / catch
match read_file(path, None, None) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData
&& e.to_string().contains("binary") => { /* iconv UTF-16->UTF-8, or use bash/xxd */ }
other => other,
} Prevention
- Convert UTF-16 exports to UTF-8 before reading
- Remember NUL in the first 8 KiB is the trigger — includes images, executables, sqlite, DER
- Use Bash tool (file/xxd/base64) for anything not plain text
When it happens
Trigger: Reading an image, executable, SQLite DB, pickle/parquet file, or any blob with a 0x00 byte in the first 8192 bytes; reading a UTF-16LE/BE text export (every other byte is 0x00).
Common situations: Agents wandering into `assets/`, `*.db`, `node_modules` binaries; Windows-origin text files saved as UTF-16; certificate/key DER files.
Related errors
- file is too large ({} bytes, max {} bytes)
- content is too large ({} bytes, max {} bytes)
- old_string and new_string must differ
- old_string not found in file
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/d6b85db485f0c1c3.
Report an issue: GitHub.