Discord.py触发AttributeError: 'NoneType' object has no attribute 'guild'如何修复
报错根因
这个AttributeError触发的原因是:当用户完全断开语音连接时,after.channel的值为None,此时直接访问after.channel.guild自然会抛出空对象无对应属性的异常。同时你的代码还有两处隐藏问题:
- 逻辑判断顺序错误,没有优先处理用户离开语音的场景
- 统计时长的代码里
userdata[total_time]没有加引号,total_time会被识别为未定义变量,后续运行到这一步还会报错
修复后的完整代码
@commands.Cog.listener() async def on_voice_state_update(self, member, before, after): if member.bot: # 优先检测是否为机器人,减少不必要的文件操作 return with open('data/voice_leaderboard.json', 'r') as file: voice_data = json.load(file) new_user = str(member.id) guild_id = str(member.guild.id) # 新增:新服务器防护,避免KeyError if guild_id not in voice_data: voice_data[guild_id] = {} if new_user not in voice_data[guild_id]: # 若用户不在当前服务器统计字典中则新增条目 voice_data[guild_id][new_user] = { "total_time" : 0, "join_time" : None} userdata = voice_data[guild_id][new_user] # 提取用户数据简化后续代码逻辑 # 调整判断顺序:先处理加入频道场景 if before.channel is None: userdata["join_time"] = round(time.time()) # 再处理完全离开频道场景 elif after.channel is None: if userdata["join_time"] is None: return # 统计本次语音时长 leave_time = time.time() passed_time = leave_time - userdata["join_time"] # 修复:total_time改为字符串键 userdata["total_time"] += passed_time userdata["join_time"] = None # 此时before、after的channel都非空,处理同服务器操作场景 elif before.channel.guild.id == after.channel.guild.id: # 同服务器切换频道/调整静音等操作不修改计时,直接返回 return # 剩余场景为跨服务器切换频道 else: if userdata["join_time"] is None: return leave_time = time.time() passed_time = leave_time - userdata["join_time"] userdata["total_time"] += passed_time userdata["join_time"] = None with open('data/voice_leaderboard.json', 'w') as update_user_data: json.dump(voice_data, update_user_data, indent=4)
关键改动说明
- 调整了判断优先级,优先处理
before.channel为None(加入)、after.channel为None(离开)的场景,避免后续判断时访问空对象的guild属性 - 新增了服务器ID不存在于统计数据的防护逻辑,避免新服务器第一次触发事件时报KeyError
- 修复了
total_time未加引号的拼写错误 - 所有空值判断改用
is None的Python规范写法,避免隐式类型转换导致的逻辑错误
内容的提问来源于stack exchange,提问作者gwendal
相关产品推荐
相关产品推荐

