You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js 16版Lambda向DynamoDB存储时间戳失败求助

问题:Lambda写入DynamoDB时触发ValidationException(测试正常,实际执行失败)

问题描述

使用Node.js 16版本的Lambda函数向DynamoDB存储数据时,本地测试用例能成功写入,但实际由Amazon Connect触发时抛出ValidationException,错误提示为Supplied AttributeValue is empty, must contain exactly one of the supported datatypes。需要存储的LastUpdateTimestamp是联系人最后更新的UTC时间字符串,格式为yyyy-mm-ddThh:mm:ssZ。

相关Lambda代码

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();

exports.handler = async (event) => {
    
    var surveyResults = {};
    var data = event.Details.ContactData.Attributes;    // extract the contact attributes from the Amazon Connect event
    
    Object.keys(data).forEach(element => {
       if (element.startsWith("survey_result_")) {
           surveyResults[element] = { S: data[element] };
       }
    });
    
    var params = {
        TableName: process.env.TABLE,
        Item: {
            contactId: { S: event.Details.ContactData.ContactId},
            date: { S: event.Details.ContactData.LastUpdateTimestamp},
            surveyId: { S: event.Details.ContactData.Attributes.surveyId },
            ...surveyResults
        }
    }
    
    try {
        await ddb.putItem(params).promise();    // write the object to the DynamoDB table
    } catch (err) {
        console.log(err);
    }

    const response = {
        statusCode: 200,
        body: JSON.stringify('OK'),
    };
    
    return response;
};

测试数据

{
  "Name": "ContactFlowEvent",
  "Details": {
    "ContactData": {
      "Attributes": {
        "surveyId": "123456",
        "survey_result_1": "4",
        "survey_result_2": "5"
      },
      "Channel": "VOICE",
      "ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
      "LastUpdateTimestamp": "2023-02-14T06:52:29Z",
      "CustomerEndpoint": {
        "Address": "+11234567890",
        "Type": "TELEPHONE_NUMBER"
      },
      "InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
      "InitiationMethod": "API",
      "InstanceARN": "arn:aws:connect:us-east-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa",
      "MediaStreams": {
        "Customer": {
          "Audio": {
            "StartFragmentNumber": "91343852333181432392682062622220590765191907586",
            "StartTimestamp": "1565781909613",
            "StreamARN": "arn:aws:kinesisvideo:us-east-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806"
          }
        }
      },
      "PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
      "Queue": null,
      "SystemEndpoint": {
        "Address": "+11234567890",
        "Type": "TELEPHONE_NUMBER"
      }
    },
    "Parameters": {}
  }
}

触发时的错误信息

2023-02-15T14:10:26.426Z    94fad25e-bcdb-443b-95c0-47640bfaba34    INFO    ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes
    at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14)
    at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
  code: 'ValidationException',
  time: 2023-02-15T14:10:26.328Z,
  requestId: 'F5JJOCF20D507JCFRO7FEMAH6VVV4KQNSO5AEMVJF66Q9ASUAAJG',
  statusCode: 400,
  retryable: false,
  retryDelay: 15.458319111471575
}

问题分析与解决方案

核心原因

错误提示明确说明:DynamoDB收到了空的属性值(AttributeValue),也就是你组装的Item里,某个字段的{ S: 值 }中,值为空或未定义。

测试数据里所有必填字段都有有效值,但实际触发时,Amazon Connect传递的event可能存在以下情况:

  • LastUpdateTimestamp字段未生成或为空
  • Attributes.surveyId未被正确传入
  • 部分survey_result_*属性为空

这些情况都会导致组装出{ S: undefined }的无效结构,触发DynamoDB的校验错误。

修复方案

方案1:添加字段校验与过滤(基于原代码修改)

在组装params前,先校验必填字段是否存在,同时过滤掉空的survey结果:

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();

exports.handler = async (event) => {
    var surveyResults = {};
    // 先获取关键节点,避免链式调用报错
    const contactData = event.Details?.ContactData;
    const attributes = contactData?.Attributes || {};

    // 校验必填字段是否存在且有值
    if (!contactData?.ContactId || !contactData?.LastUpdateTimestamp || !attributes?.surveyId) {
        console.error('缺失必填字段:', {
            ContactId: contactData?.ContactId,
            LastUpdateTimestamp: contactData?.LastUpdateTimestamp,
            surveyId: attributes?.surveyId
        });
        return { statusCode: 400, body: JSON.stringify('缺失必填字段') };
    }

    // 仅保留有值的survey结果
    Object.keys(attributes).forEach(element => {
        if (element.startsWith("survey_result_") && attributes[element]) {
            surveyResults[element] = { S: attributes[element] };
        }
    });

    var params = {
        TableName: process.env.TABLE,
        Item: {
            contactId: { S: contactData.ContactId },
            date: { S: contactData.LastUpdateTimestamp },
            surveyId: { S: attributes.surveyId },
            ...surveyResults
        }
    }

    try {
        await ddb.putItem(params).promise();
        console.log('数据保存成功');
    } catch (err) {
        console.error('数据保存失败:', err);
        return { statusCode: 500, body: JSON.stringify('数据保存失败') };
    }

    return {
        statusCode: 200,
        body: JSON.stringify('OK'),
    };
};

方案2:使用DocumentClient简化操作(推荐)

AWS SDK的DocumentClient可以自动处理数据类型映射,无需手动写{ S: 值 },大幅降低出错概率:

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient(); // 改用DocumentClient

exports.handler = async (event) => {
    var surveyResults = {};
    const contactData = event.Details?.ContactData;
    const attributes = contactData?.Attributes || {};

    if (!contactData?.ContactId || !contactData?.LastUpdateTimestamp || !attributes?.surveyId) {
        console.error('缺失必填字段:', {
            ContactId: contactData?.ContactId,
            LastUpdateTimestamp: contactData?.LastUpdateTimestamp,
            surveyId: attributes?.surveyId
        });
        return { statusCode: 400, body: JSON.stringify('缺失必填字段') };
    }

    Object.keys(attributes).forEach(element => {
        if (element.startsWith("survey_result_") && attributes[element]) {
            surveyResults[element] = attributes[element]; // 无需手动指定类型
        }
    });

    var params = {
        TableName: process.env.TABLE,
        Item: {
            contactId: contactData.ContactId,
            date: contactData.LastUpdateTimestamp,
            surveyId: attributes.surveyId,
            ...surveyResults
        }
    }

    try {
        await ddb.put(params).promise(); // 使用put方法替代putItem
        console.log('数据保存成功');
    } catch (err) {
        console.error('数据保存失败:', err);
        return { statusCode: 500, body: JSON.stringify('数据保存失败') };
    }

    return {
        statusCode: 200,
        body: JSON.stringify('OK'),
    };
};

内容的提问来源于stack exchange,提问作者Raja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 22:10:44