copy/v86 · error · StateLoadError

Invalid header:

Error message

Invalid header: 

What it means

After the length check, read_state_header reads the 4-int header block and compares the magic number at STATE_INDEX_MAGIC against STATE_MAGIC. A mismatch means the buffer is not a v86 state dump at all, so StateLoadError('Invalid header: 0x...') is thrown with the observed magic in hex.

Source

Thrown at src/state.js:217

/* @param {CPU} cpu */
export function restore_state(cpu, state)
{
    state = new Uint8Array(state);

    function read_state_header(state, check_length)
    {
        const len = state.length;

        if(len < STATE_INFO_BLOCK_START)
        {
            throw new StateLoadError("Invalid length: " + len);
        }

        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];
    }

View on GitHub (pinned to 180830d539)

Solutions

  1. Verify you're passing a v86 save-state file (correct extension and origin) to restore_state
  2. Re-create the state with the same emulator version used for loading
  3. Check the served file isn't an error page: inspect the first bytes / content-length
  4. If migrating formats, convert or re-capture the state rather than loading old binaries

Example fix

// before
emulator.restore_state(await file.arrayBuffer().then(b => new Uint8Array(b))); // any file accepted
// after
const view = new Int32Array(buf);
if (view[0] !== MAGIC) { // 0x8601CCE0-style state magic
    throw new Error("not a v86 state file");
}
emulator.restore_state(new Uint8Array(buf));
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeV86State(buf) {
    if (!(buf instanceof Uint8Array) || buf.byteLength < 12) return false;
    const magic = new Int32Array(buf.buffer, buf.byteOffset, 4)[0] >>> 0;
    return magic === STATE_MAGIC; // same constant as src/state.js
}
if (!looksLikeV86State(state)) throw new Error("not a v86 state file");

Type guard

function isV86StateDump(x) {
    return x instanceof Uint8Array && x.byteLength >= 12 &&
    (new Int32Array(x.buffer, x.byteOffset, 4)[0] >>> 0) === STATE_MAGIC;
}

Try / catch

try {
    emulator.restore_state(state);
} catch (e) {
    if (e instanceof StateLoadError && /Invalid header/.test(e.message)) {
    showError("That file is not a v86 save state.");
    } else throw e;
}

Prevention

When it happens

Trigger: Loading a file that isn't a v86 state dump (wrong file type, foreign save format, text/HTML error page) into restore_state; loading a state produced by an incompatible emulator build that wrote a different magic.

Common situations: Users picking the wrong file in a file picker (a ROM or disk image instead of a state); proxy/server error responses saved as .bin; crossing major versions where the state format's magic changed.

Related errors


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