copy/v86 · error · StateLoadError

Length doesn't match header: real=... header=...

Error message

Length doesn't match header: real=... header=...

What it means

The header records STATE_INDEX_TOTAL_LEN, the exact byte length the dump had when written. When check_length is true, read_state_header compares it with the actual buffer length and throws StateLoadError('Length doesn't match header: real=N header=M') on any mismatch, catching truncated, padded, or concatenated state buffers.

Solutions

  1. Verify the state file size matches what was originally saved (compare with the header value printed in the error)
  2. Re-download/re-export the state file completely and retry
  3. If the state is embedded in a larger blob, slice exactly the header-reported length
  4. Check the storage/transfer path for corruption (compare checksums before and after)

Example fix

// before
emulator.restore_state(bigBlob.subarray(offset)); // length may not match header
// after
const headerLen = new Int32Array(bigBlob.buffer, bigBlob.byteOffset, 4)[3];
emulator.restore_state(bigBlob.subarray(offset, offset + headerLen));
Defensive patterns

Strategy: validation

Validate before calling

function headerLengthMatches(buf) {
    if (!(buf instanceof Uint8Array) || buf.byteLength < 12) return false;
    const h = new Int32Array(buf.buffer, buf.byteOffset, 4);
    return h[3] === buf.byteLength; // STATE_INDEX_TOTAL_LEN === len
}
if (!headerLengthMatches(state)) throw new Error("state file size mismatch");

Try / catch

try {
    emulator.restore_state(state);
} catch (e) {
    if (e instanceof StateLoadError && /Length doesn't match header/.test(e.message)) {
    reacquireStateFile(); // re-download / re-export
    } else throw e;
}

Prevention

When it happens

Trigger: Passing a Uint8Array to restore_state whose byteLength differs from the value stored at STATE_INDEX_TOTAL_LEN in the dump header — truncated download, extra bytes appended, slicing the wrong range out of a bigger blob, or byte-level corruption of the header.

Common situations: Interrupted downloads/partial uploads of state files; storage layers (localStorage, IndexedDB) altering or clipping blobs; manually slicing states out of a combined file with off-by-N errors.

Related errors


AI-assisted analysis of copy/v86@180830d539 (2026-08-31). Data as JSON: /api/errors/43bb142d5b794827. Report an issue: GitHub.

Appendix: source

Thrown at src/state.js:229

        }

        const header_block = new Int32Array(state.buffer, state.byteOffset, 4);

        if(header_block[STATE_INDEX_MAGIC] !== STATE_MAGIC)
        {
            throw new StateLoadError("Invalid header: " + h(header_block[STATE_INDEX_MAGIC] >>> 0));
        }

        if(header_block[STATE_INDEX_VERSION] !== STATE_VERSION)
        {
            throw new StateLoadError(
                    "Version mismatch: dump=" + header_block[STATE_INDEX_VERSION] +
                    " we=" + STATE_VERSION);
        }

        if(check_length && header_block[STATE_INDEX_TOTAL_LEN] !== len)
        {
            throw new StateLoadError(
                    "Length doesn't match header: " +
                    "real=" + len + " header=" + header_block[STATE_INDEX_TOTAL_LEN]);
        }

        return header_block[STATE_INDEX_INFO_LEN];
    }

    function read_info_block(info_block_buffer)
    {
        const info_block = new TextDecoder().decode(info_block_buffer);
        return JSON.parse(info_block);
    }

    if(new Uint32Array(state.buffer, 0, 1)[0] === ZSTD_MAGIC)
    {
        const ctx = cpu.zstd_create_ctx(state.length);

        new Uint8Array(cpu.wasm_memory.buffer, cpu.zstd_get_src_ptr(ctx) >>> 0, state.length).set(state);

View on GitHub (pinned to 180830d539)