aws-sdk-js-v3: client-dynamodb/lib-dynamodb v3.428.0: Sending an UpdateCommand fails with TypeError

Checkboxes for prior research

Describe the bug

Since v3.428.0 updates on a DynamoDB table fail with a TypeError. It worked up to v3.427.0. The call of new UpdateCommand(...) is ok, but sending the call to DynamoDB fails. The properties of the UpdateCommand result are different between v3.428.0 and v3.427.0, which may lead to this error.

SDK version number

@aws-sdk/lib-dynamodb@3.428.0

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

Node.js 18.x

Reproduction Steps

let UpdateExpression = "SET autoDeployEnabled = :autoDeployEnabled";
if (args.config.manualDeploymentInfo !== undefined) {
  UpdateExpression += ", manualDeploymentInfo = :manualDeploymentInfo";
}
const cmd = new UpdateCommand({
  ExpressionAttributeValues: {
    ":autoDeployEnabled": args.config.autoDeployEnabled,
    ":manualDeploymentInfo": args.config.manualDeploymentInfo,
  },
  Key: { dataItemType: TestDeploymentDataItemType, repoName: args.repoName },
  ReturnValues: "ALL_NEW",
  TableName: GlobalConstants.DataItemsTableName,
  UpdateExpression,
});
const result = await DynamoDBDocumentClient.send(cmd);

Observed Behavior

The call await DynamoDBDocumentClient.send(cmd) fails with a TypeError:

TypeError: Cannot read properties of undefined (reading 'S')
    at AttributeValue.visit (node_modules/@aws-sdk/client-dynamodb/dist-cjs/models/models_0.js:620:19)
    at se_AttributeValue (node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:2948:38)
    at [path_redacted]/node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:3151:20
    at Array.reduce (<anonymous>)
    at se_ExpressionAttributeValueMap (node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:3147:34)
    at ExpressionAttributeValues (node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:3535:43)
    at applyInstruction (node_modules/@smithy/smithy-client/dist-cjs/object-mapping.js:73:33)
    at take (node_modules/@smithy/smithy-client/dist-cjs/object-mapping.js:44:9)
    at se_UpdateItemInput (node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:3529:37)
    at se_UpdateItemCommand (node_modules/@aws-sdk/client-dynamodb/dist-cjs/protocols/Aws_json1_0.js:357:27)

Expected Behavior

The call doesn’t fail. It didn’t fail with prior versions of client-dynamodb/lib-dynamodb.

Possible Solution

No response

Additional Information/Context

The properties inputKeyNodes and outputKeyNodes of the created UpdateCommand (variable cmd; see above) differ:

v3.427.0

  "inputKeyNodes": [
    { "key": "Key" },
    {
      "key": "AttributeUpdates",
      "children": { "children": [{ "key": "Value" }] }
    },
    {
      "key": "Expected",
      "children": {
        "children": [{ "key": "Value" }, { "key": "AttributeValueList" }]
      }
    },
    { "key": "ExpressionAttributeValues" }
  ],
  "outputKeyNodes": [
    { "key": "Attributes" },
    {
      "key": "ItemCollectionMetrics",
      "children": [{ "key": "ItemCollectionKey" }]
    }
  ],

with v3.428.0

  "inputKeyNodes": {
    "Key": {},
    "AttributeUpdates": { "*": { "Value": null } },
    "Expected": { "*": { "Value": null, "AttributeValueList": [] } },
    "ExpressionAttributeValues": {}
  },
  "outputKeyNodes": {
    "Attributes": {},
    "ItemCollectionMetrics": { "ItemCollectionKey": {} }
  },

inputKeyNodes and outputKeyNodes where changed/touched in this PR https://github.com/aws/aws-sdk-js-v3/pull/5306 (fix(lib-dynamodb): add e2e suite and bug fixes for lib-dynamodb)

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Reactions: 8
  • Comments: 15 (6 by maintainers)

Commits related to this issue

Most upvoted comments

The root Item is not marshalled because if it were, it would be sent as a Map:

native JS input

{
  Item: {
    PK: '...',
    column1: 1,
    column2: 'string'
  }
}

marshall if Item=AttributeValue (incorrect)

{
  Item: {
    M: {
      PK: { S: '...' },
      column1: { N: 1 },
      column2: { S: 'string' }
    }
  }
}

corrected marshalling with Item.*=AttributeValue

{
  Item: {
    PK: { S: '...' },
    column1: { N: 1 },
    column2: { S: 'string' }
  }
}

The reason this behavior changed is because the PR fixed the AttributeValue node indicators to more correctly target what fields are actual AttributeValues in need of conversion. However, as I mentioned above, I’ll try to add the top level filtering to be compatible with existing usage.

@ncino-esselman

It can’t marshal/marshall the column value of undefined because it doesn’t correspond to any DynamoDB AttributeValue type. The closest is NULL, i.e. { NULL: true } or omitting the column. { S: undefined } is not valid because the S is for string values.

Undefined can be removed from maps and sets by the removeUndefinedValues marshallOptions flag, because that does not change the AttributeValue type of the map or set. But when it is the entire value, the column cannot be inserted. Currently the TypeError is based on the SDK being unable to serialize the unexpected undefined column value.

Previously it worked because the marshall system incorrectly defined the entire Item as an AttributeValue and extracted its M value.


@maplesteve if manualDeploymentInfo is a column value and can be undefined, you are likely facing the same issue, and the workarounds above are also advised in this case.

If you performing an insert, there is a patch upcoming in #5365 which will ignore undefined columns as before. However, if you are performing an update and want to set the column’s value from something to nothing, you should use a value of null instead. Setting an undefined column in an update will, even after the fix, at best be ignored, but will not overwrite the table data as you may intend.

That makes sense! Thank you so much for your time and explanation! Also for the backwards compatibility change!!