ruvnet/ruflo · error

String length exceeds remaining buffer

Error message

String length ${len} exceeds remaining buffer

What it means

BufferReader.readString decodes a GGUF string as [u64 length][utf-8 bytes]; if the declared length exceeds the bytes remaining in the buffer, it throws rather than reading past the end. parseGgufHeader only reads the first 256 KB of the file, so this fires on truncated/corrupt files, headers whose metadata exceeds that window, or a reader desynchronized by an earlier malformed field.

Solutions

  1. Verify the file size and re-download/restore the model (compare checksums if the source publishes them)
  2. Check the magic and version first so corrupt files fail fast with a clearer error
  3. If large metadata is expected, read a bigger prefix than 256 KB before parsing
  4. Keep the reader aligned: treat unknown value types as fatal instead of skipping bytes
Defensive patterns

Strategy: try-catch

Validate before calling

const { size } = await stat(modelPath);
if (size < 4096) throw new Error(`model file suspiciously small: ${size} bytes`);
// magic/version pre-check also keeps the reader aligned
const meta = await parseGgufHeader(modelPath);

Try / catch

try { meta = await parseGgufHeader(path); }
catch (e) { if (e instanceof Error && /String length|GGUF/.test(e.message)) { rejectModel(path, e.message); } else throw e; }

Prevention

When it happens

Trigger: parseGgufHeader on a partially downloaded .gguf; a model whose metadata block is larger than the 256 KB read window; buffer misalignment after an unknown value type consumed the wrong byte count; a non-GGUF binary that happened to pass the magic check.

Common situations: Interrupted downloads of multi-GB models; git-lfs pointer files fetched as raw bytes; files from converters emitting non-standard metadata ordering.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/b7242bbcdd139014. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/appliance/gguf-engine.ts:99

  readU8(): number  { const v = this.buf.readUInt8(this.offset); this.offset += 1; return v; }
  readI8(): number  { const v = this.buf.readInt8(this.offset); this.offset += 1; return v; }
  readU16(): number { const v = this.buf.readUInt16LE(this.offset); this.offset += 2; return v; }
  readI16(): number { const v = this.buf.readInt16LE(this.offset); this.offset += 2; return v; }
  readU32(): number { const v = this.buf.readUInt32LE(this.offset); this.offset += 4; return v; }
  readI32(): number { const v = this.buf.readInt32LE(this.offset); this.offset += 4; return v; }
  readF32(): number { const v = this.buf.readFloatLE(this.offset); this.offset += 4; return v; }
  readF64(): number { const v = this.buf.readDoubleLE(this.offset); this.offset += 8; return v; }
  readU64(): bigint { const v = this.buf.readBigUInt64LE(this.offset); this.offset += 8; return v; }
  readI64(): bigint { const v = this.buf.readBigInt64LE(this.offset); this.offset += 8; return v; }
  /** Safe for values up to 2^53. Real GGUF files never exceed this for tensor/kv counts. */
  readU64AsNumber(): number { return Number(this.readU64()); }
  readBool(): boolean { return this.readU8() !== 0; }

  /** GGUF string: [length u64 LE][utf-8 bytes]. */
  readString(): string {
    const len = this.readU64AsNumber();
    if (len === 0) return '';
    if (len > this.remaining) throw new Error(`String length ${len} exceeds remaining buffer`);
    const s = this.buf.toString('utf-8', this.offset, this.offset + len);
    this.offset += len;
    return s;
  }
}

// ── GGUF Value Reading ──────────────────────────────────────

/** Read a typed scalar from the buffer (shared by value and array-element readers). */
function readScalar(reader: BufferReader, t: number): unknown {
  switch (t) {
    case GgufValueType.UINT8:   return reader.readU8();
    case GgufValueType.INT8:    return reader.readI8();
    case GgufValueType.UINT16:  return reader.readU16();
    case GgufValueType.INT16:   return reader.readI16();
    case GgufValueType.UINT32:  return reader.readU32();
    case GgufValueType.INT32:   return reader.readI32();
    case GgufValueType.FLOAT32: return reader.readF32();

View on GitHub (pinned to fa13ee4ad6)