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

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会在找到第一个存在的频道时直接退出函数,后续所有记录都不会处理,完全不符合需求。

修复方案

  1. 先把查询结果读取到本地列表,避免游标被复用:
@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  # 跳过存在的频道,继续处理下一条
  1. 添加异常捕获保障任务稳定性:
@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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 10:30:15