{"record":{"id":"02370c45916b926f","repo":"apache/beam","slug":"failed-to-delete-messages-due-to-expired-receipt-handles","errorCode":null,"errorMessage":"Failed to delete {} messages due to expired receipt handles.","messagePattern":"Failed to delete (.+?) messages due to expired receipt handles\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"sdks/java/io/amazon-web-services2/src/main/java/org/apache/beam/sdk/io/aws2/sqs/SqsUnboundedReader.java","lineNumber":613,"sourceCode":"      }\n\n      DeleteMessageBatchResponse result =\n          sqsClient.deleteMessageBatch(\n              DeleteMessageBatchRequest.builder()\n                  .queueUrl(queueUrl())\n                  .entries(pendingDeletes.values())\n                  .build());\n\n      Map<Boolean, Set<String>> failures =\n          result.failed().stream()\n              .collect(partitioningBy(this::isHandleInvalid, mapping(e -> e.id(), toSet())));\n\n      // Keep failed IDs only, but discard invalid receipt handles\n      pendingDeletes.keySet().retainAll(failures.getOrDefault(FALSE, ImmutableSet.of()));\n\n      int invalidHandles = failures.getOrDefault(TRUE, ImmutableSet.of()).size();\n      if (invalidHandles > 0) {\n        LOG.warn(\"Failed to delete {} messages due to expired receipt handles.\", invalidHandles);\n      }\n\n      retries += 1;\n    }\n  }\n\n  /** Check {@link BatchResultErrorEntry#code()} for invalid expired receipt handles. */\n  private boolean isHandleInvalid(BatchResultErrorEntry error) {\n    return RECEIPT_HANDLE_IS_INVALID.equals(error.code());\n  }\n\n  /**\n   * Messages which have been deleted (via the checkpoint finalize) are no longer in flight. This is\n   * only used for flow control and stats.\n   */\n  private void retire() {\n    long nowMsSinceEpoch = now();\n    while (true) {","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/amazon-web-services2/src/main/java/org/apache/beam/sdk/io/aws2/sqs/SqsUnboundedReader.java#L595-L631","documentation":"SQS message receipt handles expire (default 60s visibility + lifetime limits). When deleting messages after successful processing, SQS reports which IDs failed due to invalid/expired receipt handles; the reader logs this warning and drops those messages from the pending-delete list — meaning they will reappear after visibility timeout and may be reprocessed.","triggerScenarios":"deleteBatch receives a response where some message IDs are reported with an 'expired/invalid receipt handle' failure — happens when processing takes longer than the visibility timeout (or extended via withMaxRetries/visibility settings), so SQS redelivered the message and invalidated the old handle.","commonSituations":"Long batch processing exceeding VisibilityTimeout; duplicate deliveries; visibility timeout misconfigured much lower than processing time; very large batches of deletes taking too long to issue.","solutions":["Increase the queue's VisibilityTimeout to exceed worst-case per-batch processing time","Use SqsIO's configuration to extend visibility/heartbeat if supported by your Beam version","Make processing idempotent so redelivered messages are handled safely","Reduce delete batch size / issue deletes more frequently to shrink the handle age window"],"exampleFix":"// before: visibility too small for processing time\naws sqs set-queue-attributes --queue-url ... --attributes '{\"VisibilityTimeout\":\"30\"}'\n// after\naws sqs set-queue-attributes --queue-url ... --attributes '{\"VisibilityTimeout\":\"300\"}'","handlingStrategy":"retry","validationCode":"// verify receipt handle is still valid before long processing\nif (Duration.between(receivedAt, Instant.now()).getSeconds() > visibilityTimeoutSeconds) {\n  extendVisibility(client, queueUrl, receiptHandle, visibilityTimeoutSeconds);\n}","typeGuard":null,"tryCatchPattern":"try {\n  client.deleteMessage(b -> b.queueUrl(q).receiptHandle(h));\n} catch (ReceiptHandleIsInvalidException e) {\n  // handle expired: message was redelivered; rely on idempotent reprocessing\n}","preventionTips":["Set VisibilityTimeout well above worst-case batch processing time","Extend visibility (ChangeMessageVisibility) for long processing","Make consumers idempotent against redelivery","Issue deletes promptly after processing, in small batches"],"tags":["sqs","receipt-handle","visibility-timeout","duplicate-delivery"],"backgroundTag":"receipt-handle-expired","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}