janhq/jan · error · io::Error

Not a GGUF file

Error message

Not a GGUF file

What it means

read_header validates the first 4 bytes of the stream against the GGUF magic b"GGUF" and fails with InvalidData if they differ. It is the parser's file-format gate: whatever was passed in is not a GGUF file at all.

Solutions

  1. Point the code at an actual .gguf weights file (check the path in your config)
  2. Inspect the first 4 bytes with a hex dump or `head -c 4 model.gguf` to confirm the magic
  3. If it's an HTML page, the download failed - re-download from the correct URL
  4. Convert .ggml/.bin legacy models to GGUF v2/v3 before loading

Example fix

// before
let f = std::fs::File::open("models/tokenizer.json")?;
let meta = read_gguf_metadata(f)?; // Not a GGUF file
// after
let path = "models/qwen2-7b-q4_k_m.gguf";
let mut magic = [0u8; 4];
std::fs::File::open(path)?.read_exact(&mut magic)?;
assert_eq!(&magic, b"GGUF", "{} is not a GGUF file", path);
let meta = read_gguf_metadata(std::fs::File::open(path)?)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_gguf(path: &std::path::Path) -> io::Result<bool> {
    use std::io::Read;
    let mut magic = [0u8; 4];
    std::fs::File::open(path)?.read_exact(&mut magic)?;
    Ok(&magic == b"GGUF")
}
// call before read_gguf_metadata to give a friendly message

Try / catch

match read_gguf_metadata(file) {
    Err(e) if e.to_string() == "Not a GGUF file" => {
        eprintln!("{} is not a GGUF model; check the path/extension", path);
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling read_gguf_metadata or find_gguf_tensors with a reader whose first 4 bytes are not 'GGUF' - e.g. a safetensors, .bin, .ggml, or text file passed by mistake.

Common situations: Wrong file extension or wrong model directory in config, pointing at a tokenizer/config JSON instead of weights, downloading an HTML error page saved as model.gguf, .ggml/.bin legacy models.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17). Data as JSON: /api/errors/0232f76b439f942e. Report an issue: GitHub.

Appendix: source

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

        file.read_u64::<LittleEndian>()?; // offset into the data section

        if wanted.contains(&name) && !found.contains(&name) {
            found.push(name);
            if found.len() == wanted.len() {
                break;
            }
        }
    }
    Ok(found)
}

/// Header plus the whole KV section, leaving the reader positioned at the
/// start of the tensor-info block.
fn read_header<R: Read + Seek>(file: &mut BufReader<R>) -> io::Result<GgufMetadata> {
    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(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 7205d770c1)