{"record":{"id":"4184efd0ca7cb10c","repo":"ruvnet/ruflo","slug":"too-many-events-to-replay-events-length-cons","errorCode":null,"errorMessage":"Too many events to replay (${events.length}). Consider creating a snapshot.","messagePattern":"Too many events to replay \\((.+?)\\)\\. Consider creating a snapshot\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"v3/@claude-flow/shared/src/events/state-reconstructor.ts","lineNumber":76,"sourceCode":"    factory: (id: string) => T\n  ): Promise<T> {\n    const aggregate = factory(aggregateId);\n\n    // Try to load from snapshot first\n    if (this.options.useSnapshots) {\n      const snapshot = await this.eventStore.getSnapshot(aggregateId);\n      if (snapshot) {\n        this.applySnapshot(aggregate, snapshot);\n      }\n    }\n\n    // Get events after snapshot version (or all if no snapshot)\n    const events = await this.eventStore.getEvents(aggregateId, aggregate.version + 1);\n\n    // Apply events\n    for (const event of events) {\n      if (events.length > this.options.maxEventsToReplay) {\n        throw new Error(`Too many events to replay (${events.length}). Consider creating a snapshot.`);\n      }\n\n      aggregate.apply(event);\n    }\n\n    // Create snapshot if interval reached\n    if (this.options.useSnapshots && aggregate.version % this.options.snapshotInterval === 0) {\n      await this.createSnapshot(aggregate);\n    }\n\n    return aggregate;\n  }\n\n  /**\n   * Reconstruct state at a specific point in time\n   */\n  async reconstructAtTime<T extends AggregateRoot>(\n    aggregateId: string,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/shared/src/events/state-reconstructor.ts#L58-L94","documentation":"StateReconstructor refuses to rebuild an aggregate when the number of events to apply after the latest snapshot exceeds options.maxEventsToReplay. Replay cost grows linearly with event count, so the guard stops expensive rebuilds and points you at snapshotting: with useSnapshots enabled and a reasonable snapshotInterval, rehydration starts from a recent snapshot and the replay count stays small. It typically fires for long-lived aggregates with no snapshot yet or a snapshot interval larger than the replay ceiling.","triggerScenarios":"Rehydrating an aggregate whose post-snapshot event count exceeds maxEventsToReplay; useSnapshots disabled in StateReconstructorOptions; snapshotInterval set so high (or event volume so bursty) that more than maxEventsToReplay events accumulate between snapshots; first rehydrate of an old aggregate created before snapshots existed.","commonSituations":"High-write event-sourced aggregates with sparse snapshots; snapshots turned off for simplicity then hitting the ceiling as history grows; bulk rehydration jobs (rebuilding all aggregates) tripping the limit on the oldest ones.","solutions":["Create a snapshot for the affected aggregate (createSnapshot / enable useSnapshots) so future rehydrations start from a recent version","Lower options.snapshotInterval so snapshots are taken more frequently than maxEventsToReplay events accumulate","If the hardware genuinely supports it, raise options.maxEventsToReplay deliberately as a tuning decision — then snapshot soon after"],"exampleFix":"// before\nconst recon = new StateReconstructor(store, factory, {\n  useSnapshots: false,\n  maxEventsToReplay: 1000,\n});\nawait recon.rebuild(orderId); // long history -> throws\n\n// after\nconst recon = new StateReconstructor(store, factory, {\n  useSnapshots: true,\n  snapshotInterval: 500,\n  maxEventsToReplay: 1000,\n});\nawait recon.rebuild(orderId); // snapshots keep replay under the limit","handlingStrategy":"fallback","validationCode":"const recon = new StateReconstructor(eventStore, aggregateFactory, {\n  useSnapshots: true,\n  snapshotInterval: 500,\n  maxEventsToReplay: 1000,\n});\n// keep snapshotInterval well below maxEventsToReplay so this never trips","typeGuard":null,"tryCatchPattern":"try {\n  aggregate = await recon.rebuild(aggregateId);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Too many events to replay')) {\n    // fallback: rebuild with a raised ceiling, snapshot immediately, then restore the normal limit\n    const tolerant = new StateReconstructor(eventStore, aggregateFactory, {\n      useSnapshots: true,\n      snapshotInterval: 100,\n      maxEventsToReplay: Number.MAX_SAFE_INTEGER,\n    });\n    aggregate = await tolerant.rebuild(aggregateId);\n  } else throw e;\n}","preventionTips":["Enable useSnapshots and keep snapshotInterval far below maxEventsToReplay","Snapshot high-write aggregates on a schedule, not just on version-interval","Monitor per-aggregate event counts so the ceiling is raised deliberately, not discovered in production"],"tags":["event-sourcing","snapshot","replay","performance"],"backgroundTag":"event-sourcing-replay-limit","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}