musistudio/claude-code-router · error

ARCHIVE_REPLAY_UNAVAILABLE

ARCHIVE_REPLAY_UNAVAILABLE

Error message

The gateway replay executor is not available.

What it means

Thrown when the replay path is entered but no replay executor was provided to the archive service. The executor is the component that actually re-issues the archived request to the upstream agent; without it, replay cannot proceed.

Source

Thrown at packages/core/src/gateway/context-archive.ts:235

      throw contextArchiveError("ARCHIVE_INVALID_ARGUMENT", `${toolName} requires archive_id, session_token, and task.`);
    }

    const store = this.store(config);
    const rootSnapshot = store.get(archiveId);
    if (!rootSnapshot) {
      throw contextArchiveError("ARCHIVE_NOT_FOUND", `Archive ${archiveId} does not exist or has expired.`);
    }
    if (rootSnapshot.expiresAt !== undefined && rootSnapshot.expiresAt <= Date.now()) {
      throw contextArchiveError("ARCHIVE_EXPIRED", `Archive ${archiveId} has expired.`);
    }
    if (rootSnapshot.status !== "ready") {
      throw contextArchiveError("ARCHIVE_NOT_READY", `Archive ${archiveId} is ${rootSnapshot.status}.`);
    }
    if (!constantTimeEqual(rootSnapshot.tokenHash, sha256(sessionToken))) {
      throw contextArchiveError("ARCHIVE_ACCESS_DENIED", "The archive session token is invalid.");
    }
    if (!executor) {
      throw contextArchiveError("ARCHIVE_REPLAY_UNAVAILABLE", "The gateway replay executor is not available.");
    }

    const lineage = store.lineage(archiveId, maxLineageReplayDepth);
    const searchedGenerations: number[] = [];
    let lastInsufficientAnswer: { answer: string; snapshot: ArchiveSnapshot } | undefined;
    for (const snapshot of lineage) {
      if (snapshot.expiresAt !== undefined && snapshot.expiresAt <= Date.now()) {
        continue;
      }
      if (snapshot.status !== "ready") {
        continue;
      }
      const answer = await replayArchiveSnapshot(snapshot, task, config, executor);
      searchedGenerations.push(snapshot.generation);
      if (isInsufficientArchiveAnswer(answer) && snapshot.parentArchiveId) {
        lastInsufficientAnswer = { answer, snapshot };
        continue;
      }

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Provide/restore the replay executor when constructing the archive service
  2. Check the service construction path in your composition root or DI container for a missing/optional registration
  3. Add a startup assertion that the executor is bound before enabling the archive feature

Example fix

// before
const archive = new ContextArchive({ storagePath }); // no executor
await archive.ask(id, token, task); // ARCHIVE_REPLAY_UNAVAILABLE

// after
const archive = new ContextArchive({ storagePath }, replayExecutor);
await archive.ask(id, token, task);
Defensive patterns

Strategy: validation

Validate before calling

if (!executor) throw new Error('replay executor required for archive replay');
const archive = new ContextArchive(config, executor);

Type guard

function hasExecutor(x: unknown): x is ReplayExecutor {
  return !!x && typeof x === 'object' && 'answer' in x;
}

Try / catch

try { await archive.ask(id, token, task); }
catch (e) { if (e.code === 'ARCHIVE_REPLAY_UNAVAILABLE') { /* fail fast at startup instead */ } throw e; }

Prevention

When it happens

Trigger: Constructing the ContextArchive service/gateway without wiring an executor (or passing undefined), then calling ask() on a valid, ready, token-matching archive.

Common situations: DI misconfiguration where the executor binding is optional and silently omitted; test harnesses constructing the service with partial dependencies; refactors that removed executor registration.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/dfc7ab07faffd375. Report an issue: GitHub.