[Bug]: read tool misdetects file MIME type — infer crate scans entire file content, causing source files with magic-byte literals to be sent as images/PDFs
Bug Description
The read tool's detect_mime_type function passes the entire file content to infer::get(). The infer crate scans the full buffer for magic bytes, not just the file header. When a source file (e.g., .ts, .py, .js) contains a magic-byte literal or comment (such as %PDF, ‰PNG, or \x89PNG), infer misidentifies the entire file as a binary document. The file is then base64-encoded and sent to the model as an image_url / application/pdf content part, causing provider errors and wasted tokens.
Steps to Reproduce
Create a TypeScript file that contains a PDF magic-byte literal in a comment or array constant:
// File: src/media/kind.ts import type { MediaKind } from "../domain/index.js"; /** * Media kind detection by magic bytes */ export const PDF_MAGIC = [0x25, 0x50, 0x44, 0x46]; // %PDF(The literal
%PDFbytes appear at byte offset ~449 in the file.)Run forge with a model that supports vision (e.g.,
qwen3.8-max):forge -p "Read the file src/media/kind.ts and summarize it."The model receives the file as
data:application/pdf;base64,aW1wb3J0IHR5cGU...instead of plain text. The base64 decodes to the TypeScript source.Provider returns an error such as:
InternalError.Algo.InvalidParameter: The image format is illegal and cannot be opened.
Expected Behavior
detect_mime_type should only inspect the file header (first ~1 KB) when calling infer::get(). A .ts file containing a %PDF comment should be detected as text/plain (via extension fallback) and returned as text content, not base64-encoded as a PDF image.
Actual Behavior
infer::get() is called with the full file contents. Because kind.ts contains %PDF at byte 449, infer returns application/pdf. The file is then treated as a visual/binary document:
detect_mime_typereturns"application/pdf"is_visual_contentreturnstrue- The file is base64-encoded into a
data:application/pdf;base64,...URL - The TypeScript source is sent to the model as a PDF "image"
- The provider rejects it with
InvalidParameter: The image format is illegal
Root Cause
In crates/forge_services/src/tool_services/fs_read.rs, the detect_mime_type function:
fn detect_mime_type(path: &Path, contents: &[u8]) -> &str {
// infer::get() scans the ENTIRE contents buffer for magic bytes
if let Some(file_type) = infer::get(contents) {
return file_type.mime_type();
}
// ...extension fallback
}Verified with a standalone test using infer 0.22.0:
Input to infer::get() |
Result |
|---|---|
| Full file (entire contents) | application/pdf ❌ |
| First 100 bytes (header only) | None → extension fallback → text/plain ✅ |
First 450 bytes (before %PDF) |
None → text/plain ✅ |
First 500 bytes (includes %PDF) |
application/pdf ❌ |
Suggested Fix
Only pass the file header (first N bytes) to infer::get():
fn detect_mime_type(path: &Path, contents: &[u8]) -> &str {
// Only inspect the file header — infer scans the full buffer for magic
// bytes, so passing entire source files can trigger false positives when
// the file contains magic-byte literals (e.g., a .ts file with a "%PDF"
// comment).
let header = &contents[..contents.len().min(1024)];
if let Some(file_type) = infer::get(header) {
return file_type.mime_type();
}
// ...extension fallback
}Forge Version
forge 2.13.21 (commit 9b60797ef, built from source)
Operating System & Version
Ubuntu (Linux)
AI Provider
OpenAI-compatible (llmgateway routing to qwen3.8-max)
Model
qwen3.8-max
Installation Method
Built from source
Configuration
Reproduces with any provider/model that supports image input. The issue is in the read/fs_read tool's MIME detection, independent of provider config.
Source: tailcallhq/forgecode