Amazon Lex对接Node.js Lambda报Intent undefined错误排查
问题根因
Intent with name undefined not supported报错由两个代码逻辑错误直接引发,另有一个隐藏的并发bug会在前置错误修复后影响业务正确性:
- 主handler的bot名校验分支缺少
return中断。现有逻辑在判断bot名不匹配调用callback('Invalid Bot Name')后,代码会继续向下执行dispatch逻辑。只要触发Lambda的请求不是来自BestiaryProject机器人(包括Lex绑定Lambda时自动发送的连通性校验请求、误配置的其他触发器、选错模板的测试事件),请求体中就不存在currentIntent字段,读取intentRequest.currentIntent.name时会直接拿到undefined,触发不支持Intent的抛出逻辑。 - Lex版本不匹配。当前代码完全按照Lex V1的事件结构编写,如果控制台使用的是默认的Lex V2版本,V2传给Lambda的事件结构中不存在顶层
currentIntent字段,Intent名称存储在event.sessionState.intent.name路径下,槽位结构也完全不同,同样会导致读取到的intentName为undefined。 - 全局变量
page存在并发污染问题。Lambda执行环境存在复用机制,全局变量会在多个请求间共享,并发请求下page值会被互相覆盖,返回的怪物页码会出现串号错误。
修复步骤
- 先确认使用的Lex版本:
- 如果用的是Lex V1(控制台入口标注为Amazon Lex V1),首先修复主handler逻辑,给校验分支加return中断,同时增加请求结构兜底判断,将原有handler代码替换为如下内容:
exports.handler = (event, context, callback) => { try { console.log(`event.bot.name=${event.bot.name}`); // 校验不通过直接返回,终止后续逻辑执行 if (!event.bot || event.bot.name !== 'BestiaryProject') { callback('Invalid Bot Name'); return; } // 兜底校验请求结构合法性,拦截非预期请求 if (!event.currentIntent || !event.currentIntent.name) { callback('Invalid request structure, please check Lex version and trigger configuration'); return; } dispatch(event, (response) => callback(null, response)); } catch (err) { callback(err); } };
- 如果用的是Lex V2(当前控制台默认开放版本),上述V1版本的代码无法直接运行,需要将所有事件字段读取、响应结构构造的逻辑全部改写为V2对应的格式,匹配V2的事件结构规范。
- 修复全局变量并发bug:删除定义在函数外层的
let page = 0全局变量,将页码计算逻辑收敛到校验函数内部,通过返回值传递页码,避免请求间值污染。优化后的校验和业务逻辑参考如下:
function validateMonsters(monsterType, monsterName) { const monsterTypes = ['dragon', 'fiend', 'celestial', 'giant', 'magical beast', 'fey', 'undead', 'elemental']; // 统一转小写做匹配,避免用户输入大小写导致校验失败 if (monsterType && monsterTypes.indexOf(monsterType.toLowerCase()) === -1) { return buildValidationResult(false, 'monsterType', `I do not know what ${monsterType} is, would you like to try a different one?`); } // 用映射表替代冗长的if判断,直接维护怪物和页码的对应关系 const monsterPageMap = { 'angel':9, 'azata':23, 'chimera':44, 'balor':58, 'succubus':68, 'true dragon':90, 'dryad':116, 'fire elemental':124, 'water elemental':125, 'ghoul':146, 'fire giant':148, 'manticore':199, 'troll':268, 'unicorn':269, 'vampire':270, 'wyvern':282 }; const monsterNames = Object.keys(monsterPageMap); if (monsterName && monsterNames.indexOf(monsterName.toLowerCase()) === -1) { return buildValidationResult(false, 'monsterName', `I've never heard of ${monsterName}, would you like to try a different one?`); } // 校验通过时直接返回匹配到的页码 return { ...buildValidationResult(true, null, null), page: monsterName ? monsterPageMap[monsterName.toLowerCase()] : 0 }; } function searchMonsters(intentRequest, callback) { const monsterType = intentRequest.currentIntent.slots.monsterType; const monsterName = intentRequest.currentIntent.slots.monsterName; const source = intentRequest.invocationSource; if (source === 'DialogCodeHook') { const slots = intentRequest.currentIntent.slots; const validationResult = validateMonsters(monsterType, monsterName); if (!validationResult.isValid) { slots[`${validationResult.violatedSlot}`] = null; callback(elicitSlot(intentRequest.sessionAttributes, intentRequest.currentIntent.name, slots, validationResult.violatedSlot, validationResult.message)); return; } // 从校验结果中获取当前请求对应的页码,不依赖全局变量 const page = validationResult.page; callback(close(intentRequest.sessionAttributes, 'Fulfilled', { contentType: 'PlainText', content: `Thanks, the monster ${monsterName} of type ${monsterType} can be found on page ${page}` })); return; } }
- 检查Lambda触发器配置:打开Lambda控制台的触发器管理页,确认仅关联了目标Lex机器人,删除多余的测试触发器、其他服务的误配置触发器,避免非预期请求调用函数。
验证方式
代码修改完成后,先在Lambda控制台选择Lex对应的官方测试事件模板发起调用,确认不再抛出undefined相关错误、返回结构符合Lex要求后,再到Lex控制台进行端到端的对话测试。
内容的提问来源于stack exchange,提问作者jon thie
相关产品推荐
相关产品推荐

