#3812·forgecode

[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

Author: kassieclaireCreated Aug 4, 2026Updated Sep 17, 2026
Labelsstate: inactive

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

  1. Create a TypeScript file that contains a PDF magic-byte literal in a comment or array constant:

    typescript
    // 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.)

  2. 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."
  3. The model receives the file as data:application/pdf;base64,aW1wb3J0IHR5cGU... instead of plain text. The base64 decodes to the TypeScript source.

  4. 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:

rust
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) Nonetext/plain
First 500 bytes (includes %PDF) application/pdf

Suggested Fix

Only pass the file header (first N bytes) to infer::get():

rust
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.