question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

(aws-stepfunctions-tasks): DynamoAttributeValue.fromNumber() breaks when passed a JsonPath

See original GitHub issue

DynamoAttributeValue.fromNumber() synths incorrectly when passed a JsonPath.numberAt() for getting a number from a step’s input.

Example:

DynamoAttributeValue.fromNumber(JsonPath.numberAt('$.viewCount')

Synths as:

{\":viewCount\":{\"N\":\"-1.888154589708819e+289\"}}

Reproduction Steps

1. Deploy ProblemStack

import { Table, AttributeType, BillingMode } from '@aws-cdk/aws-dynamodb';
import { LogGroup, RetentionDays } from '@aws-cdk/aws-logs';
import { StateMachine, Chain, StateMachineType, LogLevel, JsonPath } from '@aws-cdk/aws-stepfunctions';
import { DynamoUpdateItem, DynamoAttributeValue, DynamoReturnValues } from '@aws-cdk/aws-stepfunctions-tasks';
import { Stack, Construct, StackProps, RemovalPolicy } from '@aws-cdk/core';


export class ProblemStack extends Stack {

  constructor(scope: Construct, id: string, props: StackProps = {}) {
    super(scope, id, props);

    const table = new Table(this, 'MyTable', {
      partitionKey: { name: 'id', type: AttributeType.STRING },
      billingMode: BillingMode.PAY_PER_REQUEST,
      removalPolicy: RemovalPolicy.DESTROY,
    });

    const updateViewCount = new DynamoUpdateItem(this, 'PutVideosViewCount', {
      table: table,
      key: {
        id: DynamoAttributeValue.fromString(JsonPath.stringAt('$.id')),
      },
      returnValues: DynamoReturnValues.ALL_NEW,
      updateExpression: 'SET viewCount = :viewCount',
      expressionAttributeValues: {
        ':viewCount': DynamoAttributeValue.fromNumber(JsonPath.numberAt('$.viewCount')),
      },
    });

    const main = Chain.start(updateViewCount);

    const stateMachineLogs = new LogGroup(this, 'MyStateMachineLogGroup', {
      retention: RetentionDays.THREE_MONTHS,
    });

    new StateMachine(this, 'MyStateMachine', {
      definition: main,
      stateMachineType: StateMachineType.STANDARD,
      tracingEnabled: true,
      logs: {
        destination: stateMachineLogs,
        includeExecutionData: true,
        level: LogLevel.ALL,
      },
    });
  }
}

2. Insert a record into the DynamoDB table

{
    "id": "foo",
    "viewCount": 10
}

3. Start state machine execution with event

{
    "id": "foo",
    "viewCount": 1234
}

What did you expect to happen?

The template should synth to extract the number from the input and then update the record in the db.

What actually happened?

The execution fails with the error:

"ExpressionAttributeValues contains invalid value: Number overflow. Attempting to store a number with magnitude larger than supported range for key :viewCount (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: c056d945-3aa5-484e-8c64-eb75b9b9041c; Proxy: null)

This is because the template was synthed as:

\"ExpressionAttributeValues\":{\":viewCount\":{\"N\":\"-1.888154589708819e+289\"}}

Environment

  • CDK CLI Version : 1.83.0
  • Framework Version:
  • Node.js Version: v12.19.0
  • OS : Linux
  • Language (Version): TypeScript Version 4.0.5

Other


This is 🐛 Bug Report

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:12
  • Comments:10 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
lodi-gcommented, Dec 7, 2021

Quite ugly workaround, but it works and does not require one to change the functions themselves, only the CDK code.

function dynamoNumberFromJson(jsonPath: string) {
  return tasks.DynamoAttributeValue.numberFromString(
    sfn.JsonPath.stringAt(`States.Format('{}', ${jsonPath})`)
  );
}

Using States.Format, it’ll “convert” the number to a string. Not happy with it but it works.

1reaction
GhostzLabcommented, Jan 14, 2021

@shivlaks Thank you for your help

When I change it to use numberFromString and stringAt. The execution still fails but with a different error:

"An error occurred while executing the state 'PutVideosViewCount' (entered at the event id #2). The Parameters '{\"Key\":{\"id\":{\"S\":\"foo\"}},\"TableName\":\"ProblemStack-MyTable794EDED1-1NWAFVLUWWLV7\",\"ExpressionAttributeValues\":{\":viewCount\":{\"N\":1234}},\"ReturnValues\":\"ALL_NEW\",\"UpdateExpression\":\"SET viewCount = :viewCount\"}' could not be used to start the Task: [The value for the field 'N' must be a STRING]"

The TaskStateEntered for the state is the following. As you can see, viewCount is a number.

{
  "name": "PutVideosViewCount",
  "input": {
    "id": "foo",
    "viewCount": 1234
  },
  "inputDetails": {
    "truncated": false
  }
}

I believe this is because the template was synthed as follows, so it’s not representing value of N.$ as a string.:

\"ExpressionAttributeValues\":{\":viewCount\":{\"N.$\":\"$.viewCount\"}}
Read more comments on GitHub >

github_iconTop Results From Across the Web

class DynamoAttributeValue · AWS CDK
Returns the DynamoDB attribute value. static booleanFromJsonPath(value), Sets an attribute of type Boolean from state input through Json path. static fromBinary ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found