Skip to content

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

Merged
leandrodamascena merged 3 commits into
aws-powertools:developfrom
Adityaj0:fix-idempotency-key-dict-values
Aug 28, 2026
Merged

fix(idempotency): is_missing_idempotency_key iterates dict keys instead of values#8391
leandrodamascena merged 3 commits into
aws-powertools:developfrom
Adityaj0:fix-idempotency-key-dict-values

Conversation

@Adityaj0

@Adityaj0 Adityaj0 commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

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.

@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
Adityaj0 and others added 2 commits August 28, 2026 16:52
…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.
@leandrodamascena
leandrodamascena force-pushed the fix-idempotency-key-dict-values branch from 3f7a26f to a959bb0 Compare August 28, 2026 15:59
@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.64%. Comparing base (f704837) to head (647f138).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #8391   +/-   ##
========================================
  Coverage    96.64%   96.64%           
========================================
  Files          296      296           
  Lines        14765    14767    +2     
  Branches      1245     1246    +1     
========================================
+ Hits         14269    14271    +2     
  Misses         361      361           
  Partials       135      135           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Adityaj0 thank you for the contribution! I rebased the PR onto the latest develop, simplified the comments, and added regression coverage for missing dictionary keys.

@sonarqubecloud

Copy link
Copy Markdown

@mergify

mergify Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@leandrodamascena
leandrodamascena merged commit a8df37d into aws-powertools:develop Aug 28, 2026
15 checks passed
@boring-cyborg

boring-cyborg Bot commented Aug 28, 2026

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

@powertools-for-aws-oss-automation

Copy link
Copy Markdown

Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience!

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

2 participants