musistudio/claude-code-router · error
ARCHIVE_EXPIRED
ARCHIVE_EXPIRED
Error message
Archive ${archiveId} has expired. What it means
Thrown by the context archive gateway when replaying an archive whose snapshot expiry timestamp has passed. The archive store keeps snapshots with an optional expiresAt; once Date.now() exceeds it, the snapshot is treated as gone even if the row still exists. It protects stale context from being replayed.
Source
Thrown at packages/core/src/gateway/context-archive.ts:226
archiveId: string;
sessionToken: string;
task: string;
}, config: ContextArchiveConfig, executor?: ContextArchiveReplayExecutor): Promise<ContextArchiveAskOutput> {
const archiveId = input.archiveId.trim();
const sessionToken = input.sessionToken.trim();
const task = input.task.trim();
const toolName = config.toolName || defaultToolName;
if (!archiveId || !sessionToken || !task) {
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;
}View on GitHub (pinned to 99f24806c6)
Solutions
- Re-create the archive (a new root snapshot) and restart the replay session with the new archiveId
- Increase the archive TTL/retention configuration when creating snapshots
- Complete replay workflows within the configured expiry window
Example fix
// before
const answer = await archive.ask(archiveId, token, task); // ARCHIVE_EXPIRED
// after
const snap = archive.inspect(archiveId);
if (snap?.expiresAt && snap.expiresAt <= Date.now()) {
const fresh = await archive.create(/* new root snapshot */);
archiveId = fresh.archiveId; token = fresh.sessionToken;
}
const answer = await archive.ask(archiveId, token, task); Defensive patterns
Strategy: validation
Validate before calling
const snap = store.get(archiveId);
if (!snap || (snap.expiresAt !== undefined && snap.expiresAt <= Date.now() + 60_000)) {
throw new Error('archive expired or expiring imminently — recreate it');
}
await archive.ask(archiveId, sessionToken, task); Type guard
function isUsableArchive(s: { status: string; expiresAt?: number } | undefined): s is { status: 'ready'; expiresAt?: number } {
return !!s && s.status === 'ready' && (s.expiresAt === undefined || s.expiresAt > Date.now());
} Try / catch
try { await archive.ask(id, token, task); }
catch (e) { if (e.code === 'ARCHIVE_EXPIRED') { /* recreate archive, restart session */ } throw e; } Prevention
- Set archive TTL comfortably larger than the maximum wall-clock span of your workflow
- Check expiresAt with a safety margin before every replay
- Persist archiveId+token pairs together with their creation time so expiry is predictable
When it happens
Trigger: Calling ask(archiveId, sessionToken, ...) after the archive's TTL elapsed — e.g. a job resumed hours later, or the archive was created with a short retention window. The snapshot exists and status is ready, but expiresAt <= Date.now().
Common situations: Long-running pipelines that pause between turns; retention/TTL config set too low for the workflow's wall-clock duration; clock skew between writer and reader.
Related errors
- ARCHIVE_NOT_READY
- Artifact origin is not the configured CCR gateway.
- Artifact URL does not use the CCR media artifact path.
- Refusing to archive unknown legacy JSON config file: ${unsup
- No available models. Configure at least one provider with a
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/2794cbc59ddf5585.
Report an issue: GitHub.