affaan-m/ECC · error · ReplayError
tool.fixture_corrupt
tool.fixture_corrupt
Error message
fixture ${key.slice(0, 16)} is not valid JSON What it means
FixtureStore.get throws ReplayError('tool.fixture_corrupt') when the fixture file exists but its contents cannot be parsed as JSON. This is a fail-closed integrity check: replay must refuse a fixture whose payload cannot be trusted instead of returning garbage. The short key prefix in the message identifies which fixture file to inspect.
Solutions
- Delete the corrupt fixture file `${key}.json` from the store directory and re-run the eval in mode 'record' to regenerate it from the live implementation.
- Inspect the file with a JSON validator (e.g. `node -e "JSON.parse(require('fs').readFileSync('<file>','utf8'))"`) to see the exact parse error before deleting, in case it is trivially fixable.
- Check git status / merge conflict markers in the fixtures directory — a conflicted or partially staged fixture is a common cause.
- Regenerate all fixtures if several are corrupt: wipe the store directory and run the full record pass once.
Example fix
// before: hand-edited fixture with trailing comma
{"response": {"temp": 21,}}
// after: regenerate via record mode (or fix to valid JSON)
const r = createReplayer(tools, { mode: 'record', store });
r.call('fetchWeather', recordedArgs); Defensive patterns
Strategy: try-catch
Validate before calling
const raw = fs.readFileSync(fixturePath, 'utf8');
try { JSON.parse(raw); } catch (e) { regenerateFixture(tool, args); } Type guard
function isParsableJson(text) {
try { JSON.parse(text); return true; } catch { return false; }
} Try / catch
try {
return replayer.call(tool, args);
} catch (e) {
if (e instanceof ReplayError && e.code === 'tool.fixture_corrupt') {
fs.unlinkSync(store.pathFor(e.key));
return recordAndReturn(tool, args);
}
throw e;
} Prevention
- Never hand-edit fixture files; regenerate them via record mode
- Avoid interrupting record runs mid-write; let record mode complete
- Resolve git conflicts in fixtures by regenerating, not by manual merge
- Add a lint/CI check that every fixture file parses as JSON
When it happens
Trigger: store.get(tool, args) reads `${key}.json` and JSON.parse throws — e.g. the file was truncated mid-write, hand-edited with a syntax error, saved with a BOM or invalid encoding, or overwritten by a non-JSON process.
Common situations: Manually editing fixture files to tweak recorded responses; a crash (Ctrl+C, OOM) during store.put's writeFileSync leaving a partial file; merge conflicts inside a fixture file resolved incorrectly; transferring fixtures between machines with encoding conversion.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- replay.no_store
- tool.fixture_mismatch
- tool.fixture_missing
- application request must be a JSON object
- bundle values must be finite JSON data
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/191891f8831c3606.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/replay.js:74
args_hash: hashValue(args),
response_hash: hashValue(response),
response,
};
fs.writeFileSync(this.pathFor(key), canonicalJson(record) + '\n', 'utf8');
return record;
}
get(tool, args) {
const key = this.key(tool, args);
const filePath = this.pathFor(key);
if (!fs.existsSync(filePath)) {
throw new ReplayError('tool.fixture_missing', `no fixture for ${tool} (${key.slice(0, 16)})`, { tool, key });
}
let record;
try {
record = JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (_error) {
throw new ReplayError('tool.fixture_corrupt', `fixture ${key.slice(0, 16)} is not valid JSON`, { tool, key });
}
if (record.tool !== tool || record.args_hash !== hashValue(args)) {
throw new ReplayError('tool.fixture_mismatch', `fixture ${key.slice(0, 16)} was recorded for different arguments`, { tool, key });
}
if (record.response_hash !== hashValue(record.response)) {
throw new ReplayError('tool.fixture_mismatch', `fixture ${key.slice(0, 16)} response hash does not match its content`, { tool, key });
}
return record;
}
}
/**
* tools: { name: { effect_class, determinism: 'deterministic'|'nondeterministic', impl(args) } }
* options: { mode: 'record'|'replay', store: FixtureStore, maxEffectClass: 'SE2', onCall(entry) }
*/
function createReplayer(tools, options = {}) {
const mode = options.mode || 'replay';
const store = options.store;View on GitHub (pinned to 8321021c54)