affaan-m/ECC · error · ReplayError
tool.fixture_missing
tool.fixture_missing
Error message
no fixture for ${tool} (${key.slice(0, 16)}) What it means
FixtureStore.get throws ReplayError('tool.fixture_missing') when no fixture file exists on disk for the content-addressed hash of (tool, args). In replay mode the replayer never calls the live implementation, so the stored fixture is the only source for the response; missing fixtures fail closed rather than silently returning something. This guards replay determinism: a test must never invent a response that was never recorded.
Solutions
- Run the same eval once in mode 'record' with a FixtureStore pointing at the same directory so the (tool, args) fixture is written, then switch back to replay.
- Check that the FixtureStore directory passed to new FixtureStore(dir) is the one containing the recorded fixtures (compare with where record mode wrote them).
- Verify the tool name and args are byte-identical to the recorded call — key ordering/extra fields change the canonical hash; print store.key(tool, args) and compare with the fixture filename.
- If fixtures are intentionally absent, guard the call: check store.has(tool, args) or catch ReplayError with code 'tool.fixture_missing' and record on demand.
Example fix
// before: replay fails, no fixture recorded yet
const r = createReplayer(tools, { mode: 'replay', store });
r.call('fetchWeather', { city: 'Paris' });
// after: record first, then replay
const mode = store.has('fetchWeather', { city: 'Paris' }) ? 'replay' : 'record';
const r = createReplayer(tools, { mode, store });
r.call('fetchWeather', { city: 'Paris' }); Defensive patterns
Strategy: fallback
Validate before calling
if (!store.has(toolName, args)) {
// record first, or skip, before calling in replay mode
const rec = createReplayer(tools, { mode: 'record', store });
rec.call(toolName, args);
} Type guard
function hasFixture(store, tool, args) {
return typeof store.has === 'function' && store.has(tool, args);
} Try / catch
try {
return replayer.call(tool, args);
} catch (e) {
if (e instanceof ReplayError && e.code === 'tool.fixture_missing') {
return recordAndReturn(tool, args); // fall back to record mode
}
throw e;
} Prevention
- Commit the fixtures directory to the repo so CI and teammates have the same fixture set
- Always run a record pass after changing a tool's arguments schema
- Use store.has(tool, args) before switching a suite to replay mode
- Keep a single shared constant for the fixture directory used by both record and replay runs
When it happens
Trigger: Calling replayer.call(name, args) in mode 'replay' when store.get(name, args) cannot find `${key}.json` in the fixture directory — i.e. the exact (tool, args) pair was never recorded via mode 'record', the store points at the wrong directory, or the fixture file was deleted.
Common situations: Running a replay-mode eval before ever running record mode; renaming a tool or changing argument shape so the hash changes; pointing options.store at a fresh/empty directory in CI; a teammate's fixtures are gitignored so they never arrive; cleaning the fixtures dir to 'reset' tests.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- replay.no_store
- tool.fixture_corrupt
- tool.fixture_mismatch
- Agents directory not found
- asset must be a local regular file
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/a87af164eb99b48b.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/replay.js:68
put(tool, args, response) {
const key = this.key(tool, args);
const record = {
key,
tool,
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;
}
}
/**View on GitHub (pinned to 8321021c54)