janhq/jan · error · io::Error

GGUF version has no readable tensor block

Error message

GGUF version {} has no readable tensor block

What it means

Raised by find_gguf_tensors when the GGUF file's version field is below 2. GGUF v1 encoded strings and dimensions with u32 lengths; llama.cpp refuses that version outright, and naively reading it as v2 would misparse names and invent garbage tensor entries. The function therefore rejects the file before attempting to walk the tensor block.

Solutions

  1. Re-download or obtain a GGUF v2+ version of the model from the source repository.
  2. Re-convert the original weights with a current llama.cpp convert script so the header is written as v2+.
  3. Check the file header's version field before loading so unsupported files are rejected early in the UI.
  4. Do not attempt to force-parse v1 as v2; the string/dimension sizes differ and produce corrupt names.

Example fix

// before: loading whatever GGUF is on disk
let tensors = find_gguf_tensors(&file, "token_embd")?;
// after: check version first
let meta = read_gguf_metadata(&file)?;
if meta.version < 2 { return Err(ServerError::InvalidArgument("GGUF v1 not supported; re-download as v2+".into())); }
let tensors = find_gguf_tensors(&file, "token_embd")?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_gguf_v2_plus(path: &std::path::Path) -> Result<(), ServerError> {
    let meta = read_gguf_metadata(path).map_err(ServerError::Io)?;
    if meta.version < 2 {
        return Err(ServerError::InvalidArgument(format!("GGUF v{} unsupported; re-download as v2+", meta.version)));
    }
    Ok(())
}

Try / catch

match find_gguf_tensors(&file, name) {
    Err(e) if e.to_string().contains("has no readable tensor block") => {
        prompt_user("This model uses legacy GGUF v1. Please download a v2+ build.");
    }
    other => other,
}

Prevention

When it happens

Trigger: find_gguf_tensors is called with a parsed GGUF metadata whose meta.version == 1 (or otherwise < 2), typically a very old GGML-era GGUF file produced before the v2 format change.

Common situations: Users point the plugin at ancient model files downloaded years ago (early llama.cpp GGUF dumps) that predate the v2/v3 format; converted legacy models retain version 1 headers.

Related errors


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

Appendix: source

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

/// every import to answer a yes/no question.
///
/// Only the first split of a sharded model is read, so a tensor living in a
/// later shard is reported absent -- the same limit upstream's
/// `common_speculative_types_from_gguf` documents.
pub fn find_gguf_tensors<R: Read + Seek>(
    reader: R,
    wanted: &[String],
) -> io::Result<Vec<String>> {
    let mut file = BufReader::new(reader);
    let meta = read_header(&mut file)?;

    if wanted.is_empty() {
        return Ok(Vec::new());
    }
    // v1 sized strings and dimensions with u32s. llama.cpp refuses that
    // version outright, and reading it as v2 would invent names.
    if meta.version < 2 {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("GGUF version {} has no readable tensor block", meta.version),
        ));
    }
    if meta.tensor_count > MAX_TENSORS {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("tensor count {} is unreasonably large", meta.tensor_count),
        ));
    }

    let mut found: Vec<String> = Vec::new();
    for i in 0..meta.tensor_count {
        let name = read_gguf_string(&mut file).map_err(|e| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                format!("Failed to read name for tensor {}: {}", i, e),
            )

View on GitHub (pinned to 7205d770c1)