DynamoDB Streams: ExpiredIteratorException
TL;DR — Um shard iterator do GetShardIterator só é válido por 15 minutos. Se seu consumidor pausou, processou um lote lentamente, ou manteve um iterator ao longo de um reinício, a próxima chamada GetRecords lança isto. Recupere-se solicitando um novo iterator — use AFTER_SEQUENCE_NUMBER com o último sequence number que você processou para retomar exatamente de onde parou.
O que significa
ExpiredIteratorException: The provided iterator exceeds the maximum age allowed.Ler um stream do DynamoDB diretamente é um loop de dois passos: GetShardIterator te dá um handle de posição dentro de um shard, e cada chamada GetRecords o consome e retorna o próximo (NextShardIterator). Cada um desses handles expira 15 minutos depois de ser emitido. A exceção não significa que os dados sumiram — registros de stream vivem por 24 horas — ela só significa que este handle de posição está obsoleto e você precisa de um novo.
Por que acontece
- Processamento lento entre polls — o trabalho feito por lote (escritas, chamadas externas) leva mais de 15 minutos antes do próximo
GetRecords. - Um consumidor pausado ou suspenso — um worker parado, um deploy longo, ou uma sessão de debugger segurando o loop.
- Armazenar iterators — persistir a string do iterator (em um banco ou checkpoint) e reutilizá-la depois; iterators são efêmeros, sequence numbers não.
- Backoff longo demais — um loop de error-retry que dormiu além da janela.
Como corrigir
Capture-a e re-semeie o iterator a partir do seu último checkpoint — rastreie o
SequenceNumberdo último registro que você processou, depois:const {ShardIterator} = await streams.send( new GetShardIteratorCommand({ StreamArn: streamArn, ShardId: shardId, ShardIteratorType: 'AFTER_SEQUENCE_NUMBER', SequenceNumber: lastProcessedSequenceNumber }) );Ainda sem checkpoint?
TRIM_HORIZONreprocessa o shard a partir do registro disponível mais antigo;LATESTpula para a nova atividade.Faça checkpoint de sequence numbers, nunca de iterators — o sequence number é o ponto de retomada durável.
Mantenha o loop de poll apertado — faça o trabalho pesado por registro de forma assíncrona (enfileire-o) para que a cadência do
GetRecordsfique bem dentro dos 15 minutos.Prefira consumidores gerenciados — event source mappings do Lambda e o DynamoDB Streams Kinesis Adapter (KCL) gerenciam iterators e checkpoints por você; loops de
GetRecordsfeitos à mão são onde esta exceção vive.
Certifique-se de que seu tratamento é idempotente: retomar de um checkpoint pode reentregar um registro que você processou pela metade. O stream em si contém cada registro exatamente uma vez, mas um consumidor que re-semeia a partir de seu último checkpoint efetivamente processa pelo-menos-uma-vez.
Depurar o que um stream de fato emitiu é muito mais fácil quando você consegue ver os itens mudando — o app desktop DynoTable mostra o estado ao vivo de um item para que você possa combinar registros de stream com os dados. Estimando o custo de leitura de um replay de recuperação? A calculadora de preços do DynamoDB te dá cobertura.
Erros relacionados
- TrimmedDataAccessException — você ficou para trás da janela de retenção de 24 horas (dados de fato perdidos).
- Stream not enabled
- Invalid StreamArn
- Aprenda: DynamoDB Streams
Referências
- GetShardIterator — Amazon DynamoDB Streams API Reference
- GetRecords — Amazon DynamoDB Streams API Reference
- Change data capture for DynamoDB Streams — Amazon DynamoDB Developer Guide
Verificado pela última vez em 2026-07-13 contra a documentação oficial da AWS vinculada acima.