Skip to content

fix(idempotency): is_missing_idempotency_key iterates dict keys instead of values - #8391

Open
Adityaj0 wants to merge 1 commit into
aws-powertools:developfrom
Adityaj0:fix-idempotency-key-dict-values
Open

fix(idempotency): is_missing_idempotency_key iterates dict keys instead of values#8391
Adityaj0 wants to merge 1 commit into
aws-powertools:developfrom
Adityaj0:fix-idempotency-key-dict-values

Conversation

@Adityaj0

@Adityaj0 Adityaj0 commented Aug 15, 2026

Copy link
Copy Markdown

Issue number: closes #8390

Summary

Changes

is_missing_idempotency_key iterated data directly for dict input:

if isinstance(data, (tuple, list, dict)):
    return all(x is None for x in data)

for x in data over a dict walks its keys, not its values. This is correct for list/tuple, but wrong for dict — a dict produced by a JMESPath multi-select expression (e.g. event_key_jmespath="{user: headers.user_id, order: body.order_id}") over fields that are all absent from the event resolves to {"user": None, "order": None}: ordinary non-None keys, all-None values. The key-based check incorrectly reports this as "not missing."

Consequence: with raise_on_no_idempotency_key=True, the safety check silently doesn't fire. With the default False, no warning is emitted and the all-None dict gets hashed into a real idempotency key, causing unrelated invocations that both fail to populate those fields to collapse onto the same key and get incorrectly deduplicated.

Fix: iterate data.values() for dict input.

if isinstance(data, dict):
    return all(x is None for x in data.values())
elif isinstance(data, (tuple, list)):
    return all(x is None for x in data)

Added is_missing_idempotency_key({"user": None, "order": None}) and is_missing_idempotency_key({"user": "abc"}) cases to the existing test_is_missing_idempotency_key in tests/functional/idempotency/_boto3/test_idempotency.py. The existing test only covered {None: None} (None as the key), which happens to still pass under the buggy key-iterating code — that's why it never caught this. Confirmed the new assertion fails against the pre-fix code and passes with the fix:

$ python -m pytest tests/functional/idempotency/_boto3/test_idempotency.py -q
108 passed

$ python -m pytest tests/functional/idempotency --ignore=tests/functional/idempotency/_redis -q
128 passed

(_redis excluded from my local run only because of an unrelated missing multiprocess dependency in my environment, not related to this change.)

User experience

Before: a dict-shaped idempotency key extraction (multi-select JMESPath) whose values are all missing is silently treated as present, either bypassing raise_on_no_idempotency_key entirely or generating a real (and collision-prone, since it's a constant hash) idempotency key from empty data.

After: correctly detected as missing, matching the existing behavior for list/tuple/scalar inputs.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…ad of values

is_missing_idempotency_key iterated `data` directly for dict input, which
walks its keys, not its values. For a dict whose values are all None but
whose keys are ordinary non-None strings -- exactly what a JMESPath
multi-select expression like '{user: headers.user_id, order: body.order_id}'
produces when the referenced event fields are absent -- this returns False
("not missing") when it should return True.

With raise_on_no_idempotency_key=True, the safety check that's supposed to
raise IdempotencyKeyError in this situation silently doesn't fire. With the
default False, no warning is emitted and the persistence layer hashes the
all-None dict into a real idempotency key, so unrelated invocations that
both fail to populate those fields collapse onto the same idempotency key
and get incorrectly deduplicated against each other.

The existing test only covered a dict of {None: None} (None as the key),
which happens to still pass under the old key-iterating behavior and so
never caught this. Iterate data.values() for dict input instead, and add
a test covering the realistic non-None-keys/all-None-values case.
@Adityaj0
Adityaj0 requested a review from a team as a code owner August 15, 2026 22:39
@boring-cyborg boring-cyborg Bot added the tests label Aug 15, 2026
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/S Denotes a PR that changes 10-29 lines, ignoring generated files. label Aug 15, 2026
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Denotes a PR that changes 10-29 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: is_missing_idempotency_key iterates dict keys instead of values, missing all-None-value payloads

1 participant