affaan-m/ECC · error · ReplayError

replay.no_store

replay.no_store

Error message

a FixtureStore is required

What it means

createReplayer throws ReplayError('replay.no_store') when options.store is falsy. Both record and replay modes need a FixtureStore: record writes fixtures into it and replay reads them. The constructor requires it unconditionally so a misconfigured suite cannot appear to pass while actually skipping persistence.

Solutions

  1. Construct and pass a FixtureStore: createReplayer(tools, { store: new FixtureStore('fixtures/eval'), mode }).
  2. Verify the store-producing helper actually returns a FixtureStore instance and not undefined/null (log it before createReplayer).
  3. Make the fixture directory a required, validated config value so it cannot silently become undefined.
  4. If the store depends on an optional feature, fail earlier with a clear message instead of letting the spread drop the option.

Example fix

// before
const replayer = createReplayer(tools, { mode: 'record' });

// after
const store = new FixtureStore(path.join(__dirname, 'fixtures'));
const replayer = createReplayer(tools, { mode: 'record', store });
Defensive patterns

Strategy: validation

Validate before calling

const store = new FixtureStore(process.env.FIXTURE_DIR || path.join(__dirname, 'fixtures'));
if (!(store instanceof FixtureStore)) throw new Error('fixture store not initialized');
createReplayer(tools, { mode, store });

Type guard

function isFixtureStore(v) {
  return v instanceof FixtureStore;
}

Try / catch

try {
  return createReplayer(tools, { mode, store });
} catch (e) {
  if (e instanceof ReplayError && e.code === 'replay.no_store') {
    throw new Error('FixtureStore must be constructed and passed as options.store');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling createReplayer(tools) with no options, or with options.store undefined/null — e.g. the FixtureStore was never constructed, a factory function returning null on failure, or a conditional `...(useStore && { store })` spread evaluating false.

Common situations: Copy-pasting an example that omitted the store; refactoring away the fixture directory constant so `new FixtureStore(dir)` is never called; a config loader returning null when a path is missing; forgetting that the store argument is required, not optional.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/d35667cd15b03bb2. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/eval-harness/replay.js:98

      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)