fix(idempotency): is_missing_idempotency_key iterates dict keys instead of values - #8391
Conversation
…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.
3f7a26f to
a959bb0
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
leandrodamascena
left a comment
There was a problem hiding this comment.
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.
|
|
Tick the box to add this pull request to the merge queue (same as
|
|
Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience! |
|
Awesome work, congrats on your first merged pull request and thank you for helping improve everyone's experience! |



Issue number: closes #8390
Summary
Changes
is_missing_idempotency_keyiterateddatadirectly fordictinput:for x in dataover adictwalks its keys, not its values. This is correct forlist/tuple, but wrong fordict— 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-Nonekeys, all-Nonevalues. 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 defaultFalse, 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()fordictinput.Added
is_missing_idempotency_key({"user": None, "order": None})andis_missing_idempotency_key({"user": "abc"})cases to the existingtest_is_missing_idempotency_keyintests/functional/idempotency/_boto3/test_idempotency.py. The existing test only covered{None: None}(Noneas 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:(
_redisexcluded from my local run only because of an unrelated missingmultiprocessdependency 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_keyentirely 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.