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
- Point the code at an actual .gguf weights file (check the path in your config)
- Inspect the first 4 bytes with a hex dump or `head -c 4 model.gguf` to confirm the magic
- If it's an HTML page, the download failed - re-download from the correct URL
- 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
- Check the magic bytes before parsing any model file
- Keep model paths in config pointing at .gguf weights, not tokenizer/config JSONs
- Detect HTML error pages saved as .gguf after failed downloads
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
- Array length is unreasonably large
- Error reading metadata entry
- Failed to read key for metadata entry
- Failed to read name for tensor
- GGUF version has no readable tensor block
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)