janhq/jan · error · io::Error

String length {} is unreasonably large

Error message

String length {} is unreasonably large

What it means

Returned by `read_gguf_string` when the u64 little-endian length of a GGUF string exceeds 1 MiB (1024 * 1024 bytes). The parser refuses to allocate a buffer of that size as a guard against corrupt length fields that would cause OOM. Real GGUF metadata keys and most string values are well under this bound.

Source

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

) -> io::Result<(String, String)> {
    let key = read_gguf_string(reader).map_err(|e| {
        io::Error::new(
            io::ErrorKind::InvalidData,
            format!("Failed to read key for metadata entry {}: {}", index, e),
        )
    })?;

    let value_type_u32 = reader.read_u32::<LittleEndian>()?;
    let value_type = GgufValueType::try_from(value_type_u32)?;
    let value = read_gguf_value(reader, value_type)?;

    Ok((key, value))
}

fn read_gguf_string<R: Read + ReadBytesExt>(reader: &mut R) -> io::Result<String> {
    let len = reader.read_u64::<LittleEndian>()?;
    if len > (1024 * 1024) {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("String length {} is unreasonably large", len),
        ));
    }
    let mut buf = vec![0u8; len as usize];
    reader.read_exact(&mut buf)?;
    String::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}

fn read_gguf_value<R: Read + Seek + ReadBytesExt>(
    reader: &mut R,
    value_type: GgufValueType,
) -> io::Result<String> {
    match value_type {
        GgufValueType::Uint8 => Ok(reader.read_u8()?.to_string()),
        GgufValueType::Int8 => Ok(reader.read_i8()?.to_string()),
        GgufValueType::Uint16 => Ok(reader.read_u16::<LittleEndian>()?.to_string()),
        GgufValueType::Int16 => Ok(reader.read_i16::<LittleEndian>()?.to_string()),

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Check stream alignment: confirm the immediately preceding value was read with the correct width per the GGUF spec.
  2. If the field is legitimately a large string (chat template, tokenizer data), raise the cap to e.g. 16 MiB and re-test.
  3. Hex-dump the region around the failure to confirm whether the length bytes look like a plausible ASCII-size value or random garbage.
  4. If the file is corrupted, re-download from a trusted source.

Example fix

// before
if len > (1024 * 1024) {
    return Err(io::Error::new(InvalidData, format!("String length {} is unreasonably large", len)));
}

// after - larger cap plus alignment hint in the message
const MAX_STR_LEN: u64 = 16 * 1024 * 1024;
if len > MAX_STR_LEN {
    let pos = reader.stream_position().unwrap_or(0);
    return Err(io::Error::new(InvalidData,
        format!("string len {} at byte {} exceeds cap {} (likely misalignment)", len, pos, MAX_STR_LEN)));
}
Defensive patterns

Strategy: validation

Validate before calling

fn plausibly_aligned(reader: &mut impl BufRead) -> bool {
    // heuristic: peek the next u64 length; if huge, the stream is likely misaligned
    if let Ok(len) = reader.read_u64::<LittleEndian>() { len <= 1024 * 1024 } else { false }
}

Type guard

null

Try / catch

match read_gguf_string(reader) {
    Ok(s) => Ok(s),
    Err(e) if e.to_string().contains("unreasonably large") => {
        Err(CorruptFile("oversized string length".into()))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Reading a metadata key or string value whose length field decodes to a value greater than 1,048,576. This typically means the byte stream is misaligned (a previous field was read with the wrong width) or the length bytes themselves are corrupted. It can also fire on a maliciously crafted file designed to OOM the reader, though the cap defeats that.

Common situations: Stream misalignment after an array element was skipped with the wrong stride; an endianness mismatch on the length field; a quantized file from a buggy converter that wrote the wrong length. Rarely, a legitimately huge string value (e.g. a tokenizer chat template) could approach 1 MiB, in which case the cap is too tight.

Related errors


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