fix(stepfunctions-tasks): mapFromJsonPath produces invalid M.$.$ key for tokenized JsonPath#37965
Conversation
…for tokenized JsonPath DynamoAttributeValue.mapFromJsonPath hardcoded the key as 'M.$', but the Step Functions JsonPath renderer auto-appends '.$' to any key whose value is a JsonPath token. The result was the invalid key 'M.$.$', which is rejected by Step Functions at deploy time. Sibling factories (listFromJsonPath, fromString) store the bare type letter and let the renderer add the '.$' exactly once. This change drops the hardcoded suffix so mapFromJsonPath behaves consistently with listFromJsonPath. The existing unit test was bypassing the bug by passing a literal '$.path' string instead of wrapping in JsonPath.stringAt; it now wraps the input the same way the listFromJsonPath test does. fixes aws#21149
There was a problem hiding this comment.
The pull request linter fails with the following errors:
❌ Fixes must contain a change to an integration test file and the resulting snapshot.
If you believe this pull request should receive an exemption, please comment and provide a justification. A comment requesting an exemption should contain the text Exemption Request. Additionally, if clarification is needed, add Clarification Request to a comment.
✅ A exemption request has been requested. Please wait for a maintainer's review.
|
Exemption Request This PR fixes a synth-time bug — Adding a new integ test for this fix would not provide meaningful additional coverage over the unit test rewrite at Also note: the unrelated |
Issue # (if applicable)
Closes #21149.
Reason for this change
DynamoAttributeValue.mapFromJsonPath(JsonPath.stringAt('$.Item.M'))synthesizes the invalid CloudFormation key"M.$.$": "$.Item.M"instead of"M.$": "$.Item.M". Step Functions rejects the resulting state machine at deploy time. The bug is atpackages/aws-cdk-lib/aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts:225— the factory hardcodes the key as'M.$', but the Step Functions JsonPath renderer atjson-path.ts:218-225auto-appends.$to any key whose value is a JsonPath token, producing the double suffix.Sibling factories
fromString("S.$") andlistFromJsonPath("L.$") work correctly because they store the bare type letter ({ S: value },{ L: value }) and let the renderer add.$exactly once.The issue was filed in 2022 and has remained reproducible through 2025 per follow-up comments. @kaizencc identified the bug location in 2022 ("we are adding at least one
$... I bet we don't want to always add$") but the fix never landed.Description of changes
One-line fix in
aws-stepfunctions-tasks/lib/dynamodb/shared-types.ts:225— changenew DynamoAttributeValue({ 'M.$': value })tonew DynamoAttributeValue({ M: value }). This matcheslistFromJsonPathexactly.Unit test rewrite in
aws-stepfunctions-tasks/test/dynamodb/shared-types.test.ts(the'map from json path'case). The pre-existing test passed a literal'$.path'string rather than wrapping insfn.JsonPath.stringAt('$.path').jsonPathString()only detects CDKJsonPathTokeninstances, so the literal-string call bypassed the buggy code path entirely — letting the test pass while real users (who passJsonPath.stringAt(...)) hit the bug. The updated test now wraps the input viasfn.JsonPath.stringAt(m), mirroring the correct pattern already used by the'from list with json path'test, and asserts the expected'M.$': mrendering. Pre-fix, this assertion sees'M.$.$': mand fails — so the test is a real regression guard.Backwards compatibility. Per
AGENTS.mdon validation-that-breaks-existing-input: "Input that was accepted by synth but always failed at deploy time → feature flag NOT required; fail-fast synth-time validation is preferred." The buggy synth outputM.$.$is rejected by Step Functions at deploy time, so any app currently callingmapFromJsonPath(JsonPath.stringAt(...))is already broken — the fix only changes synth for code paths that never deploy.Edge case worth flagging for reviewers:
mapFromJsonPathcalled with a literal'$.path'(noJsonPath.stringAtwrap) previously emitted"M.$": "$.path", which Step Functions could resolve as a path lookup. After this fix it emits"M": "$.path"(a literal string).listFromJsonPathalready exhibits the same behavior — bare{ L: value }only renders to"L.$"if the value is aJsonPathToken. The JSDoc@param value Json path that specifies state input to be usedimplies a token-encoded JsonPath, so this aligns the API with both its sibling and its documented contract. Calling out explicitly so a maintainer can weigh in if a feature flag is preferred.Alternative considered and rejected: refactoring
renderStringto detect already-suffixed keys (endsWith('.$')) and avoid double-suffixing. Rejected as too broad — it would touch every*FromJsonPathfactory and the central renderer, when the bug is local to one factory.Describe any new or updated permissions being added
None.
Description of how you validated changes
npx jest --testPathPattern aws-stepfunctions-tasks/test/dynamodb/shared-types→ 25/25 pass, including the updated'map from json path'case.npx jest --testPathPattern aws-stepfunctions-tasks/test/dynamodb→ 34/34 pass acrossdelete-item,get-item,put-item,update-item,shared-types.npx jest --testPathPattern aws-stepfunctions/test/fields.test→ 36/36 pass.packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/that currently exercisesmapFromJsonPath. If the PR linter requires an integ snapshot change, requesting exemption: this fixes a synth-time bug whose output is rejected by Step Functions at deploy time, and there is no existing integ test exercising the affected factory.Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license