affaan-m/ECC · error · ReplayError
replay.bad_mode
replay.bad_mode
Error message
mode must be record or replay, got ${mode} What it means
createReplayer throws ReplayError('replay.bad_mode') when options.mode is neither 'record' nor 'replay' (the default is 'replay'). The replayer only supports these two modes because they define whether the live implementation may run. Failing fast at construction prevents silently running a suite in an unintended mode.
Solutions
- Set mode to exactly 'record' or 'replay' (lowercase) in the createReplayer options.
- If the mode comes from an env var/CLI flag, normalize and validate it before passing: const mode = process.env.EVAL_MODE === 'record' ? 'record' : 'replay'.
- Omit the mode option entirely to use the safe default ('replay').
- Check for empty-string env overrides — `EVAL_MODE= npm test` yields '' which fails this check; delete the var or default it.
Example fix
// before
const replayer = createReplayer(tools, { mode: process.env.EVAL_MODE, store });
// after
const mode = process.env.EVAL_MODE === 'record' ? 'record' : 'replay';
const replayer = createReplayer(tools, { mode, store }); Defensive patterns
Strategy: validation
Validate before calling
const MODES = ['record', 'replay']; const mode = MODES.includes(process.env.EVAL_MODE) ? process.env.EVAL_MODE : 'replay';
Type guard
function isReplayMode(v) {
return v === 'record' || v === 'replay';
} Try / catch
try {
return createReplayer(tools, { mode, store });
} catch (e) {
if (e instanceof ReplayError && e.code === 'replay.bad_mode') {
throw new Error(`EVAL_MODE must be 'record' or 'replay', received: ${JSON.stringify(mode)}`);
}
throw e;
} Prevention
- Normalize env/CLI mode input against a whitelist before constructing the replayer
- Default to 'replay' (the safe mode) whenever the input is unrecognized
- Watch for empty-string env vars overriding defaults
- Centralize mode parsing in one helper instead of repeating it per suite
When it happens
Trigger: Passing options.mode with a typo (e.g. 'Record', 'rec', 'capture'), reading the mode from an env var or CLI flag that is unset or misspelled (process.env.EVAL_MODE may be undefined-turned-string, empty string ''), or passing a non-string value.
Common situations: CI pipeline sets MODE=replay-vs-record style values; developers add a 'live' or 'passthrough' mode that the library does not support; a config file uses 'record-mode' naming; empty string env var overriding the default.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- unknown role " "; expected one of
- artifact has invalid modality binding
- assigneeKind must be 'agent' or 'human'.
- Choose at least one guided harness: Claude, Codex, or Kimi.
- CV effect has an invalid subject anchor
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/5e826ed13c02b558.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/replay.js:95
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;
const maxRank = envelope.effectRank(options.maxEffectClass || 'SE2');
if (!['record', 'replay'].includes(mode)) {
throw new ReplayError('replay.bad_mode', `mode must be record or replay, got ${mode}`);
}
if (!store) {
throw new ReplayError('replay.no_store', 'a FixtureStore is required');
}
for (const [name, tool] of Object.entries(tools)) {
if (!envelope.EFFECT_CLASSES.includes(tool.effect_class)) {
throw new ReplayError('replay.bad_declaration', `tool ${name} must declare an effect_class`);
}
if (!['deterministic', 'nondeterministic'].includes(tool.determinism)) {
throw new ReplayError('replay.bad_declaration', `tool ${name} must declare determinism`);
}
}
const calls = [];
const emit = (entry) => {
calls.push(entry);
if (typeof options.onCall === 'function') {
options.onCall(entry);View on GitHub (pinned to 8321021c54)