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
Fange es ab und säe den Iterator aus deinem letzten Checkpoint neu — verfolge die
SequenceNumberdes 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_HORIZONverarbeitet den Shard ab dem ältesten verfügbaren Record neu;LATESTspringt zu neuer Aktivität.Checkpointe Sequenznummern, niemals Iteratoren — die Sequenznummer ist der dauerhafte Resume-Punkt.
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.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
- TrimmedDataAccessException — du bist hinter das 24-Stunden-Retention-Fenster zurückgefallen (Daten tatsächlich weg).
- Stream not enabled
- Invalid StreamArn
- Learn: DynamoDB Streams
References
- GetShardIterator — Amazon DynamoDB Streams API Reference
- GetRecords — Amazon DynamoDB Streams API Reference
- Change data capture for DynamoDB Streams — Amazon DynamoDB Developer Guide
Zuletzt verifiziert am 2026-07-13 gegen die oben verlinkte offizielle AWS-Dokumentation.