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 %PDF bytes 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_type returns "application/pdf"
is_visual_content returns true
- 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.
Bug Description
The
readtool'sdetect_mime_typefunction passes the entire file content toinfer::get(). Theinfercrate 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),infermisidentifies the entire file as a binary document. The file is then base64-encoded and sent to the model as animage_url/application/pdfcontent 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:
(The literal
%PDFbytes appear at byte offset ~449 in the file.)Run forge with a model that supports vision (e.g.,
qwen3.8-max):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:
Expected Behavior
detect_mime_typeshould only inspect the file header (first ~1 KB) when callinginfer::get(). A.tsfile containing a%PDFcomment should be detected astext/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. Becausekind.tscontains%PDFat byte 449,inferreturnsapplication/pdf. The file is then treated as a visual/binary document:detect_mime_typereturns"application/pdf"is_visual_contentreturnstruedata:application/pdf;base64,...URLInvalidParameter: The image format is illegalRoot Cause
In
crates/forge_services/src/tool_services/fs_read.rs, thedetect_mime_typefunction:Verified with a standalone test using
infer 0.22.0:infer::get()application/pdf❌None→ extension fallback →text/plain✅%PDF)None→text/plain✅%PDF)application/pdf❌Suggested Fix
Only pass the file header (first N bytes) to
infer::get():Forge Version
forge 2.13.21(commit9b60797ef, 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_readtool's MIME detection, independent of provider config.