Skip to content

Bug: Idempotency key in idempotent_function fails to serialize dates/UUIDs #8065

@jontmy

Description

@jontmy

Expected Behaviour

The @idempotent_function decorator should be able to serialize any idempotency keys which are Pydantic models containing dates or UUIDs (or more precisely, any type that can be serialized with model_dump(mode="json")).

Current Behaviour

aws_lambda_powertools.utilities.idempotency.exceptions.IdempotencyPersistenceLayerError: Failed to save in progress record to idempotency store - (Object of type UUID is not JSON serializable)

Code snippet

# src/idempotency_serialization/main.py

from uuid import UUID

from aws_lambda_powertools.utilities import idempotency
from pydantic import BaseModel

idempotency_config = idempotency.IdempotencyConfig()
idempotency_persistence_layer = idempotency.DynamoDBPersistenceLayer(
    table_name="idempotency"
)


class IdempotencyKey(BaseModel):
    req_id: UUID


def handle_request(req: IdempotencyKey) -> None:
    return _impl(req=req)


@idempotency.idempotent_function(
    data_keyword_argument="req",
    config=idempotency_config,
    persistence_store=idempotency_persistence_layer,
)
def _impl(req: IdempotencyKey) -> None:
    pass

# tests/test_idempotency.py

from uuid import uuid4

import boto3
from moto import mock_aws


@mock_aws
def test_idempotency() -> None:
    client = boto3.client("dynamodb")
    client.create_table(
        TableName="idempotency",
        KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
        AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
        BillingMode="PAY_PER_REQUEST",
    )

    from idempotency_serialization.main import IdempotencyKey, handle_request

    handle_request(IdempotencyKey(req_id=uuid4()))

Possible Solution

The _prepare_data function in base.py should probably use .model_dump(mode="json") instead of leaving mode unset, which defaults to "python" and does not serialize UUIDs to strings.

def _prepare_data(data: Any) -> Any:
    """Prepare data for json serialization.

    We will convert Python dataclasses, pydantic models or event source data classes to a dict,
    otherwise return data as-is.
    """

    # Convert from dataclasses
    if hasattr(data, "__dataclass_fields__"):
        import dataclasses

        return dataclasses.asdict(data)

    # Convert from Pydantic model
    if callable(getattr(data, "model_dump", None)):
        return data.model_dump()

    # Convert from event source data class
    if callable(getattr(data, "dict", None)):
        return data.dict()

    # Return raw event
    return getattr(data, "raw_event", data)

Steps to Reproduce

mkdir idempotency-serialization
cd idempotency-serialization
uv init --build-backend uv
uv add aws-lambda-powertools pydantic moto pytest
mkdir -p src/idempotency_serialization
mkdir -p tests
touch src/idempotency_serialization/main.py
touch tests/test_idempotency.py

Then copy the snippets above into src/idempotency_serialization/main.py and tests/test_idempotency.py, and run uv run pytest.

You should get this error:

FAILED tests/test_idempotency.py::test_idempotency - aws_lambda_powertools.utilities.idempotency.exceptions.IdempotencyPersistenceLayerError: Failed to save in progress record to idempotency store - (Object of type UUID is not JSON serializable)

Powertools for AWS Lambda (Python) version

latest

AWS Lambda function runtime

3.14

Packaging format used

PyPi

Debugging logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriagePending triage from maintainers

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions