ruvnet/ruflo · error
KV cache file truncated
Error message
KV cache file truncated
What it means
While walking the entry list (count from the header), loadKvCache throws as soon as an entry header (8 bytes) or an entry body (keyLen+valLen bytes) would read past the end of the file. The declared entries exceed the bytes actually present — an interrupted write, a partial copy, or disk-full during persistKvCache.
Solutions
- Regenerate the cache: delete the truncated file and re-run persistKvCache (optionally after a warm-up generate)
- If you manage cache files yourself, copy/move them atomically (temp file + rename)
- Catch this error and continue with a cold cache rather than aborting the session
Example fix
// before
await engine.loadKvCache(cachePath);
// after
try {
await engine.loadKvCache(cachePath);
} catch (e) {
if (e instanceof Error && e.message.includes('truncated')) {
await rm(cachePath, { force: true });
await engine.persistKvCache(cachePath);
} else throw e;
} Defensive patterns
Strategy: try-catch
Try / catch
try { await engine.loadKvCache(cachePath); }
catch (e) {
if (e instanceof Error && e.message.includes('truncated')) {
await rm(cachePath, { force: true });
await engine.persistKvCache(cachePath);
} else throw e;
} Prevention
- Copy/move cache files atomically (temp + rename); never copy a file being written
- Ensure persistKvCache completes before shutdown (graceful drain)
- Monitor disk space where caches live; quota exhaustion mid-write is a common cause
When it happens
Trigger: Process crash or kill during persistKvCache's single writeFile; copying the cache file while it is still being written; disk quota hit mid-write; a corrupted entry-count field.
Common situations: Hard-killed appliance processes; backup jobs copying live cache files; shared storage with quotas; non-atomic file management scripts.
Related errors
- KV cache file too small
- String length exceeds remaining buffer
- Invalid KV cache magic: 0x
- No KV cache output path specified
- Unknown GGUF array element type
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/a0b9eeff23a6ff20.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/appliance/gguf-engine.ts:401
if (this.config.verbose) console.log(`[gguf-engine] KV cache persisted: ${this.kvCache.size} entries`);
}
/** Restore KV cache from an RVF-compatible binary file. */
async loadKvCache(inputPath: string): Promise<void> {
const data = await readFile(inputPath);
if (data.length < 44) throw new Error('KV cache file too small');
const magic = data.readUInt32LE(0);
if (magic !== RVKV_MAGIC) throw new Error(`Invalid KV cache magic: 0x${magic.toString(16)}`);
const version = data.readUInt32LE(4);
if (version !== RVKV_VERSION) throw new Error(`Unsupported KV cache version: ${version}`);
const entryCount = data.readUInt32LE(40);
let offset = 44;
const entries = new Map<string, Buffer>();
for (let i = 0; i < entryCount; i++) {
if (offset + 8 > data.length) throw new Error('KV cache file truncated');
const keyLen = data.readUInt32LE(offset);
const valLen = data.readUInt32LE(offset + 4);
offset += 8;
if (offset + keyLen + valLen > data.length) throw new Error('KV cache file truncated');
entries.set(data.toString('utf-8', offset, offset + keyLen), Buffer.from(data.subarray(offset + keyLen, offset + keyLen + valLen)));
offset += keyLen + valLen;
}
// Verify footer hash (mandatory)
if (offset + 32 > data.length) {
throw new Error('KV cache file missing SHA256 footer');
}
const stored = data.subarray(offset, offset + 32);
const computed = createHash('sha256').update(data.subarray(44, offset)).digest();
if (!stored.equals(computed)) throw new Error('KV cache integrity check failed: hash mismatch');
this.kvCache = entries;
if (this.config.verbose) console.log(`[gguf-engine] KV cache loaded: ${entries.size} entries`);View on GitHub (pinned to fa13ee4ad6)