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
- Verify you're passing a v86 save-state file (correct extension and origin) to restore_state
- Re-create the state with the same emulator version used for loading
- Check the served file isn't an error page: inspect the first bytes / content-length
- 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
- Filter file pickers to the correct state extension
- Check magic bytes before restore
- Label state files clearly so users pick the right one
- Don't persist error pages as state files
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
- Invalid length:
- Length doesn't match header: real=... header=...
- Invalid info block length:
- Version mismatch: dump=... we=...
AI-assisted analysis of copy/v86@180830d539 (2026-08-31).
Data as JSON: /api/errors/668f32a398576ef5.
Report an issue: GitHub.