DynamoDB Streams: ExpiredIteratorException

TL;DR — Ein Shard-Iterator von GetShardIterator ist nur 15 Minuten gültig. Wenn dein Consumer pausierte, einen Batch langsam verarbeitete oder einen Iterator über einen Neustart hielt, wirft der nächste GetRecords-Aufruf dies. Erhole dich, indem du einen neuen Iterator anforderst — nutze AFTER_SEQUENCE_NUMBER mit der letzten von dir verarbeiteten Sequenznummer, sodass du genau dort fortsetzt, wo du aufgehört hast.

Was es bedeutet

ExpiredIteratorException: The provided iterator exceeds the maximum age allowed.

Einen DynamoDB-Stream direkt zu lesen ist eine zweistufige Schleife: GetShardIterator gibt dir ein Positions-Handle in einen Shard, und jeder GetRecords-Aufruf verbraucht es und gibt das nächste zurück (NextShardIterator). Jedes dieser Handles läuft 15 Minuten nach seiner Ausgabe ab. Die Exception bedeutet nicht, dass Daten weg sind — Stream-Records leben 24 Stunden — sie bedeutet nur, dass dieses Positions-Handle veraltet ist und du ein neues brauchst.

Warum es passiert

  • Langsame Verarbeitung zwischen Polls — die pro Batch geleistete Arbeit (Writes, externe Aufrufe) dauert länger als 15 Minuten vor dem nächsten GetRecords.
  • Ein pausierter oder ausgesetzter Consumer — ein gestoppter Worker, ein langer Deploy oder eine Debugger-Session, die die Schleife hält.
  • Iteratoren speichern — den Iterator-String (in einer DB oder einem Checkpoint) zu persistieren und später wiederzuverwenden; Iteratoren sind flüchtig, Sequenznummern nicht.
  • Backoff zu lang geworden — eine Error-Retry-Schleife, die über das Fenster hinaus schlief.

So behebst du es

  1. Fange es ab und säe den Iterator aus deinem letzten Checkpoint neu — verfolge die SequenceNumber des letzten von dir verarbeiteten Records, dann:

    const {ShardIterator} = await streams.send(
      new GetShardIteratorCommand({
        StreamArn: streamArn,
        ShardId: shardId,
        ShardIteratorType: 'AFTER_SEQUENCE_NUMBER',
        SequenceNumber: lastProcessedSequenceNumber
      })
    );

    Noch kein Checkpoint? TRIM_HORIZON verarbeitet den Shard ab dem ältesten verfügbaren Record neu; LATEST springt zu neuer Aktivität.

  2. Checkpointe Sequenznummern, niemals Iteratoren — die Sequenznummer ist der dauerhafte Resume-Punkt.

  3. Halte die Poll-Schleife knapp — erledige schwere Arbeit pro Record asynchron (stelle sie in eine Warteschlange), sodass die GetRecords-Kadenz gut innerhalb von 15 Minuten bleibt.

  4. Bevorzuge verwaltete Consumer — Lambda-Event-Source-Mappings und der DynamoDB-Streams-Kinesis-Adapter (KCL) verwalten Iteratoren und Checkpoints für dich; selbstgebaute GetRecords-Schleifen sind der Ort, wo diese Exception lebt.

Stelle sicher, dass deine Behandlung idempotent ist: Das Fortsetzen von einem Checkpoint kann einen Record, den du halb verarbeitet hast, erneut zustellen. Der Stream selbst enthält jeden Record genau einmal, aber ein Consumer, der von seinem letzten Checkpoint neu sät, verarbeitet faktisch at-least-once.

Zu debuggen, was ein Stream tatsächlich ausgab, ist viel einfacher, wenn du die Items sich ändern sehen kannst — die DynoTable-Desktop-App zeigt den Live-Zustand eines Items, sodass du Stream-Records gegen die Daten abgleichen kannst. Die Read-Kosten eines Nachhol-Replays schätzen? Der DynamoDB-Preisrechner hat dich abgedeckt.

Verwandte Fehler

References

Zuletzt verifiziert am 2026-07-13 gegen die oben verlinkte offizielle AWS-Dokumentation.

Mit DynamoDB ohne die Console arbeiten

DynoTable ist ein schneller Desktop-Client für DynamoDB — durchsuche Tabellen, führe SQL-artige Queries aus und bearbeite Items lokal.