AWS Lambda部署Python Telegram Bot触发KeyError:'body'报错排查
问题场景
使用Python(telebot库)、AWS Lambda与API Gateway开发Telegram Bot过程中,调试Lambda函数时出现无法定位根因的运行错误。
原始Lambda代码
import telebot import datetime TOKEN = 'xxx' def lambda_handler(event, context): bot = telebot.TeleBot(TOKEN) # Extract the message key over payload's body message = json.loads(event['body']) print(message) # Split between three variables bellow chat_id = message['chat']['id'] # Chat ID will guide your chatbot reply sender = message['from']['first_name'] # Sender's first name, registered by user's telegram app text = message['text'] # The message content if text.lower().strip() == "/time": current_time = datetime.strftime(datetime.now(), "%H:%M:%S") bot.send_message(chat_id, "Right now its {} UTC.".format(current_time)) else: pass
运行报错信息
{ "errorMessage": "'body'", "errorType": "KeyError", "stackTrace": [ " File \"/var/task/lambda_function.py\", line 10, in lambda_handler\n message = json.loads(event['body'])\n" ] }
测试用事件JSON
{ "update_id": 0000000, "message": { "message_id": 000000, "from": { "id": 00000000, "is_bot": false, "first_name": "myname", "last_name": "mysurname", "username": "sursurname", "language_code": "it" }, "chat": { "id": 000000, "first_name": "myname", "last_name": "mysurname", "username": "sursurname", "type": "private" }, "date": 1654697178, "forward_from": { "id": 00000000, "is_bot": false, "first_name": "mysurname", "last_name": "mysurname", "username": "sursurname", "language_code": "it" }, "forward_date": 0000000000, "text": "ciao" } }
根因分析
- 触发
KeyError: 'body'的直接原因:当前使用的测试事件是Telegram原生推送的更新结构,本身不存在body字段。event['body']是API Gateway开启Lambda代理集成时才会封装的字段,原始Telegram请求内容会被序列化为字符串放在这个字段下,直接用原生Telegram事件做Lambda入参测试时,自然找不到这个键。 - 代码存在3个隐藏bug,就算解决
body字段问题也会运行失败:- 调用了
json.loads()但没有导入json标准库,会触发NameError - 更新内容层级读取错误:
json.loads(event['body'])拿到的是完整的Telegram更新对象,消息实体存放在update['message']下,直接读取message['chat']['id']会再次触发KeyError - datetime方法调用错误:
import datetime导入的是模块,strftime和now()是datetime.datetime类的方法,原写法会触发AttributeError
- 调用了
- 原代码没有返回API Gateway要求的标准响应格式,线上运行时会被API Gateway判定为执行失败,返回502错误。
修复方案
- 补全缺失的依赖导入,增加场景兼容逻辑,同时支持本地原生Telegram事件测试、线上API Gateway转发请求两种场景
- 修正消息层级读取逻辑、datetime方法调用逻辑
- 补充标准Lambda返回值,适配API Gateway要求
修复后的可运行代码:
import telebot import datetime import json TOKEN = 'xxx' def lambda_handler(event, context): bot = telebot.TeleBot(TOKEN) # 自动适配两种入参场景 if 'body' in event: # 线上API Gateway转发场景,解析body里的原始更新内容 update = json.loads(event['body']) else: # 本地测试场景,直接传入的是Telegram原生更新对象 update = event print(update) # 正确读取消息层级 message = update['message'] chat_id = message['chat']['id'] sender = message['from']['first_name'] text = message['text'] if text.lower().strip() == "/time": current_time = datetime.datetime.strftime(datetime.datetime.now(), "%H:%M:%S") bot.send_message(chat_id, f"Right now its {current_time} UTC.") # 返回API Gateway要求的标准响应 return { 'statusCode': 200, 'body': 'success' }
额外排查注意点
- 线上部署时必须开启API Gateway的Lambda代理集成,否则请求内容不会被封装到
event['body']字段中 - 如果要模拟API Gateway的请求格式做本地测试,需要把完整的Telegram更新内容序列化为字符串,放在事件的
body字段下,示例格式如下:
{ "body": "{\"update_id\": 123456, \"message\": {\"message_id\": 1, \"from\": {...}, \"chat\": {...}, \"date\": 1654697178, \"text\": \"/time\"}}" }
内容的提问来源于stack exchange,提问作者Difettoso
相关产品推荐
相关产品推荐

