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

AWS SDK v3写入DynamoDB无报错却失败,求排查方案

问题

我正在开发一个AWS Lambda函数,负责将数据写入DynamoDB表,已在SAM模板中配置了相关权限:

- Effect: "Allow"
  Action:
     - "dynamodb:GetItem"
     - "dynamodb:PutItem"
     - "dynamodb:UpdateItem"
     - "dynamodb:DeleteItem"
  Resource:
   - !Join ["/", [!Sub "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table", '{{resolve:ssm:/examples/example/tableName}}'] ]

调用DynamoDB写入数据的逻辑如下:

dynamoStorage.ts

export const setThings = async (accountId: string, clientKey: string, things: EnhancedSearchFilter[]): Promise<void> => {
    if (!tableName) {
        return Promise.reject(new Error(DynamoError.InvalidTableName));
    }
    // 批量写入所有数据到dynamo
    const promises = things.map((thing) => putThing(accountId, clientKey, thing));
    return Promise.allSettled(promises).then((results) => {
        if (results.some((result) => result.status === 'rejected')) {
            customLogger.error('Failed to put things', { clientKey, things });
            throw new Error('Failed to set all filters');
        }
    });
};

调用DynamoDB的putThing函数

export const putThing = async (accountId: string, clientKey: string, thing: EnhancedThing): Promise<void> => {

    if (!tableName) {
        return Promise.reject(new Error(DynamoError.InvalidTableName));
    }

    const thingRef = createThingRef(accountId, thing.id);
    const params: PutCommandInput = {
        Item: thingToItem(clientKey, accountId, thing),
        TableName: tableName
    };

    try {
        await dynamoClient.send(new PutCommand(params));
        return Promise.resolve();
    } catch (cause) {
        customLogger.error('Failed to put thing', { clientKey, thingRef, cause });
        throw new Error(`Failed to put thing`, { cause });
    }
};

在CloudWatch中多次看到上述错误日志,但日志中的cause显示为空对象{}。请问这种场景下我的错误处理是否存在问题?若有该如何改进?若无,为何DynamoDB写入失败却不返回错误信息?


分析与解决方案

错误处理的问题及改进

  1. 日志序列化导致cause为空
    AWS SDK抛出的DynamoDB错误是自定义类实例,包含大量非枚举属性(如$metadata),而多数日志工具默认只序列化对象的可枚举属性,直接传递原始错误会导致日志中cause显示为空。

改进方法:手动提取错误关键信息,转为可序列化的普通对象:

catch (cause) {
    const errorDetails = {
        name: cause.name,
        message: cause.message,
        code: cause.code, // 比如ConditionalCheckFailedException、AccessDeniedException等错误码
        requestId: cause.$metadata?.requestId,
        statusCode: cause.$metadata?.httpStatusCode
    };
    customLogger.error('Failed to put thing', { clientKey, thingRef, errorDetails });
    throw new Error(`Failed to put thing`, { cause });
}
  1. 批量写入的错误信息缺失细节
    setThings函数仅抛出通用错误,未记录具体失败的项和原因,不利于定位问题。

改进方法:收集失败结果,补充到日志和错误信息中:

return Promise.allSettled(promises).then((results) => {
    const failedResults = results.filter(r => r.status === 'rejected');
    if (failedResults.length > 0) {
        const failedItems = things.filter((_, idx) => failedResults[idx].status === 'rejected');
        const errorMessages = failedResults.map(r => (r as PromiseRejectedResult).reason.message);
        customLogger.error('Partial failure when putting things', { clientKey, failedItems, errorMessages });
        throw new Error(`Failed to set ${failedResults.length} filters: ${errorMessages.join(', ')}`);
    }
});
  1. 错误传递的完整性优化
    虽然使用了{ cause }传递原始错误,但部分日志库或运行环境可能对错误对象处理有限。如果不需要封装错误,可以直接抛出原始错误;若需要封装,可将原始错误的关键信息附加到新错误的属性中。

为什么DynamoDB写入失败却显示空cause

核心原因是错误对象序列化限制:AWS SDK v3的错误对象包含不可枚举属性,默认日志序列化无法识别这些属性,导致日志中cause呈现为空对象。

额外排查点:

  • 检查customLogger配置,是否开启了深度序列化,或者是否有过滤错误属性的规则;
  • 验证SAM模板中SSM参数解析是否正确,确保资源ARN指向目标DynamoDB表,执行角色权限无遗漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 10:15:35