janhq/jan · error · io::Error

Not a GGUF file

Error message

Not a GGUF file

What it means

Thrown by `read_gguf_metadata` after reading the first 4 bytes of the file and comparing them against the ASCII bytes `b"GGUF"`. The GGUF format always starts with this 4-byte magic header (offset 0). If the comparison fails, the file is not a valid GGUF container, so the parser aborts before reading version/tensor/metadata counts. It is returned as `io::Error` with kind `InvalidData`.

Source

Thrown at src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/helpers.rs:13

use byteorder::{LittleEndian, ReadBytesExt};
use std::convert::TryFrom;
use std::io::{self, BufReader, Read, Seek};

use super::types::{GgufMetadata, GgufValueType};

pub fn read_gguf_metadata<R: Read + Seek>(reader: R) -> io::Result<GgufMetadata> {
    let mut file = BufReader::new(reader);

    let mut magic = [0u8; 4];
    file.read_exact(&mut magic)?;
    if &magic != b"GGUF" {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "Not a GGUF file",
        ));
    }

    let version = file.read_u32::<LittleEndian>()?;
    let tensor_count = file.read_u64::<LittleEndian>()?;
    let metadata_count = file.read_u64::<LittleEndian>()?;

    let mut metadata_map = std::collections::HashMap::new();
    for i in 0..metadata_count {
        match read_metadata_entry(&mut file, i) {
            Ok((key, value)) => {
                metadata_map.insert(key, value);
            }
            Err(e) => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Verify the file is actually GGUF: run `head -c 4 file.gguf | xxd` and confirm it prints `47 47 55 46` (`GGUF`).
  2. Re-download the model from a trusted source if the magic bytes do not match; a corrupted or HTML-error-page download is the usual cause.
  3. Confirm the path handed to `read_gguf_metadata` is the model file and not a directory, symlink target, or unrelated artifact.
  4. If you must support legacy GGML, parse it with a separate GGML reader rather than the GGUF parser.

Example fix

// before
let meta = read_gguf_metadata(File::open(path)?)?;

// after
let mut f = File::open(path)?;
let mut magic = [0u8; 4];
f.read_exact(&mut magic)?;
if &magic != b"GGUF" {
    return Err(io::Error::new(
        io::ErrorKind::InvalidData,
        format!("{} is not a GGUF file (magic={:?})", path.display(), magic),
    ));
}
f.seek(SeekFrom::Start(0))?;
let meta = read_gguf_metadata(f)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_gguf(path: &Path) -> bool {
    let mut f = match std::fs::File::open(path) {
        Ok(f) => f,
        Err(_) => return false,
    };
    let mut magic = [0u8; 4];
    f.read_exact(&mut magic).is_ok() && &magic == b"GGUF"
}

// before calling read_gguf_metadata:
if !is_gguf(&path) {
    return Err(format!("{} is not a GGUF file", path.display()));
}

Type guard

null

Try / catch

match read_gguf_metadata(File::open(&path)?) {
    Ok(meta) => Ok(meta),
    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains("Not a GGUF") => {
        Err(UserError::InvalidModelFile(path.display().to_string()))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling `read_gguf_metadata(reader)` with any non-GGUF input: a GGML (legacy) file, a safetensors/ckpt/bin file, a partially-downloaded GGUF whose header is truncated to fewer than 4 bytes, or an empty file (read_exact then returns UnexpectedEof before the comparison is reached). Passing a file opened on a path that resolved to a directory or a text file produces the same outcome.

Common situations: Downloading a model from a URL that returned an HTML error page, mistaking a GGML v1 file for GGUF, pointing the loader at a `.bin` LLaMA checkpoint, or a truncated download where the magic check coincidentally still fails because the bytes are HTML/JSON. Also seen when the file handle is a different file than expected (path race, wrong symlink).

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/0232f76b439f942e. Report an issue: GitHub.