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

  1. If it really is text in UTF-16, convert first: `iconv -f UTF-16 -t UTF-8 file > file.u8` and read that.
  2. For genuine binaries, use the Bash tool (e.g. `file`, `xxd | head`, `base64`) instead of the Read tool.
  3. 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

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


AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18). Data as JSON: /api/errors/d6b85db485f0c1c3. Report an issue: GitHub.