Bug: idempotent functions return None on replay when the stored response is falsy (0, False, "", [], {})

Đang mở
#8,700 3 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@thebadalov đang làm issue này rồi.

Từ ngày 29/10/2026.

  • #8701 của @thebadalov — đang mở

Đánh giá

Issue này chưa được đánh giá.

Mô tả

bug idempotency
Expected Behaviour

When an idempotent function returns a falsy value (0, False, "", [], {}), a retry with the same payload should return that same value from the persistence store.

Current Behaviour

The first call returns the value and it is stored correctly, but every replay returns None.

IdempotencyHandler._handle_for_status reads the stored response with:

serialized_response = self.output_serializer.from_dict(response_dict) if response_dict else None

so any falsy response_dict becomes None. #8090 pointed out this line together with the write path in _get_function_response. The write path was changed to is not None, but the read path still uses a truthiness check, so the value is saved and then dropped on replay.

Code snippet
from aws_lambda_powertools.utilities.idempotency import idempotent_function
from aws_lambda_powertools.utilities.idempotency.exceptions import (
    IdempotencyItemAlreadyExistsError,
    IdempotencyItemNotFoundError,
)
from aws_lambda_powertools.utilities.idempotency.persistence.base import BasePersistenceLayer, DataRecord


class InMemoryPersistenceLayer(BasePersistenceLayer):
    def __init__(self):
        super().__init__()
        self.records = {}

    def _get_record(self, idempotency_key) -> DataRecord:
        if idempotency_key not in self.records:
            raise IdempotencyItemNotFoundError
        return self.records[idempotency_key]

    def _put_record(self, data_record: DataRecord) -> None:
        if data_record.idempotency_key in self.records:
            raise IdempotencyItemAlreadyExistsError
        self.records[data_record.idempotency_key] = data_record

    def _update_record(self, data_record: DataRecord) -> None:
        self.records[data_record.idempotency_key] = data_record

    def _delete_record(self, data_record: DataRecord) -> None:
        self.records.pop(data_record.idempotency_key, None)


persistence_layer = InMemoryPersistenceLayer()


@idempotent_function(data_keyword_argument="order", persistence_store=persistence_layer)
def count_items(order):
    return len(order["items"])


order = {"order_id": "o-1", "items": []}
print(count_items(order=order))  # 0
print(count_items(order=order))  # None, expected 0

I see the same with CachePersistenceLayer on a real Redis server: 0, False, "", [] and {} all come back as None on replay, while None and truthy values are returned correctly.

Possible Solution

Use is not None, matching the write path:

serialized_response = self.output_serializer.from_dict(response_dict) if response_dict is not None else None

DataRecord.response_json_as_dict() already returns None when there is no stored data, and a function that returns None is stored as null, so that case keeps returning None.

Steps to Reproduce

Run the snippet above. Any persistence layer is affected, since the check is in the shared handler.

Powertools for AWS Lambda (Python) version

latest (3.37.0, also on develop)

AWS Lambda function runtime

3.13

Packaging format used

PyPi

Debugging logs
N/A, the replay returns None without an error
Ngôn ngữ chính
Python
Star
3.3k
Fork
511
Merge trung bình
3 ngày 8 giờ
Pull request đã merge (30 ngày)
92

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của aws-powertools/powertools-lambda-python

Tất cả issue của aws-powertools/powertools-lambda-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.