Discord Python Bot MySQL报错:'NoneType' object is not subscriptable
问题:Discord Python Bot定时任务遍历MySQL游标时触发TypeError
我开发的Discord Python Bot里有个每12小时执行的ticket_clear定时任务,逻辑是查询tickets表的auth_id和chann_id,遍历结果检查Discord频道是否存在,不存在就删除对应数据库记录。但执行for (auth_id, chann_id) in cur:时触发TypeError,错误提示TypeError: 'NoneType' object is not subscriptable,追踪到mysql connector的_fetch_row方法里self._rows是NoneType。数据库里至少有40条记录,而且别处同类查询代码运行正常,怀疑问题和Discord相关。
完整报错栈
Traceback (most recent call last): File "/usr/local/lib/python3.9/dist-packages/discord/client.py", line 409, in _run_event await coro(*args, **kwargs) File "/home/kobe/RyBet/rybet.py", line 80, in on_ready await load() File "/home/kobe/RyBet/rybet.py", line 92, in load for (auth_id, chann_id) in cur: File "/usr/local/lib/python3.9/dist-packages/mysql/connector/cursor_cext.py", line 787, in fetchone return self._fetch_row() File "/usr/local/lib/python3.9/dist-packages/mysql/connector/cursor_cext.py", line 739, in _fetch_row row = self._rows[self._next_row] TypeError: 'NoneType' object is not subscriptable
出错的定时任务代码
@ loop(hours=12) async def ticket_clear(): # try: cur.execute("SELECT auth_id, chann_id from tickets") for (auth_id, chann_id) in cur: channel = client.get_channel(chann_id) if (channel): return else: cur.execute( f"DELETE from tickets WHERE chann_id=%s", ((chann_id),)) conn.commit() # except: # print("Reconnecting") # await reconnect()
正常运行的同类查询代码
cur.execute("SELECT auth_id, chann_id from tickets") for (auth_id, chann_id) in cur: tickets[chann_id] = auth_id
问题根源
和Discord无关,是MySQL游标复用导致的结果集丢失:你在遍历原查询游标时,内部又调用同一个游标执行DELETE语句,这会直接重置游标状态,原查询的结果集被清空,self._rows变成None,后续遍历自然报错。另外原代码还有逻辑错误:if (channel): return会在找到第一个存在的频道时直接退出函数,后续所有记录都不会处理,完全不符合需求。
修复方案
- 先把查询结果读取到本地列表,避免游标被复用:
@loop(hours=12) async def ticket_clear(): cur.execute("SELECT auth_id, chann_id from tickets") # 一次性取出所有结果到本地列表 ticket_records = cur.fetchall() for (auth_id, chann_id) in ticket_records: channel = client.get_channel(chann_id) if not channel: # 频道不存在才执行删除 cur.execute("DELETE from tickets WHERE chann_id=%s", (chann_id,)) conn.commit() else: continue # 跳过存在的频道,继续处理下一条
- 添加异常捕获保障任务稳定性:
@loop(hours=12) async def ticket_clear(): try: cur.execute("SELECT auth_id, chann_id from tickets") ticket_records = cur.fetchall() for (auth_id, chann_id) in ticket_records: channel = client.get_channel(chann_id) if not channel: cur.execute("DELETE from tickets WHERE chann_id=%s", (chann_id,)) conn.commit() except Exception as e: print(f"清理ticket任务出错: {str(e)}") # 可选:触发数据库重连 # await reconnect()
额外注意
- 不要用f-string拼接SQL,保持参数化查询的写法(你原代码的参数化写法冗余,直接传
(chann_id,)即可)。 - 确认数据库连接在定时任务执行时是活跃状态,避免因连接超时导致的游标异常。
内容的提问来源于stack exchange,提问作者Zaiga
相关产品推荐
相关产品推荐

