Description
In react_roles/react_roles.py, there is incorrect error handling that checks if fetch_message() returns None, but this method never returns None - it raises exceptions instead.
Location
File: react_roles/react_roles.py
Lines: 55-56
Current Code
message = await channel.fetch_message(message_id)
if not message: # ❌ fetch_message never returns None!
log.error(f"Message {message_id} not found, removing from config...")
# cleanup code
The Problem
Discord.py's fetch_message() has two possible outcomes:
- Success: Returns a
discord.Message object (never None)
- Failure: Raises
discord.NotFound exception
The current code assumes it can return None, which is incorrect. This means:
- The
if not message: check never triggers
- If a message is deleted,
fetch_message() will raise discord.NotFound instead
- The cleanup code is never reached
- The exception propagates up, potentially crashing the bot
From Discord.py Documentation
async def fetch_message(message_id: int) -> Message:
"""Retrieves a single Message from the destination.
Raises
------
discord.NotFound
The specified message was not found.
discord.Forbidden
You do not have the permissions required to get a message.
discord.HTTPException
Retrieving the message failed.
"""
Notice there's no mention of returning None - it always returns Message or raises an exception.
Impact
- Incorrect error handling: Deleted messages cause exceptions instead of cleanup
- Config pollution: Deleted messages stay in the config forever
- Potential bot crash: Unhandled
discord.NotFound can propagate
- Misleading code: Future developers might copy this incorrect pattern
Fix Required
Replace the None check with proper exception handling:
try:
message = await channel.fetch_message(message_id)
except discord.NotFound:
log.error(f"Message {message_id} not found, removing from config...")
async with self.config.channel(channel).watching() as watching:
if str(message_id) in watching:
del watching[str(message_id)]
continue # Skip to next message
except discord.Forbidden:
log.error(f"No permission to fetch message {message_id}")
continue
except discord.HTTPException as e:
log.error(f"Failed to fetch message {message_id}: {e}")
continue
Similar Issues to Check
Search for other instances of this pattern:
grep -n "fetch_message" *.py | grep "if not"
Also check:
fetch_user() - also raises exceptions, never returns None
fetch_channel() - same behavior
fetch_guild() - same behavior
get_message() (cache lookup) - this one DOES return None
Priority
MEDIUM - Causes incorrect behavior and config pollution, but doesn't always crash.
Testing
After fixing:
- Set up a reaction role on a message
- Delete the message
- Restart the bot (to reload reaction role listeners)
- Verify the deleted message is cleaned from config
- Check logs for proper error messages (no crashes)
Additional Note
For clarity, use get_message() when you want to check the cache without fetching:
# Check cache (returns None if not cached)
message = channel.get_message(message_id)
if not message:
# Try fetching
try:
message = await channel.fetch_message(message_id)
except discord.NotFound:
# Handle missing message
Related
This is part of a comprehensive code review. See other issues for additional improvements.
References
Description
In
react_roles/react_roles.py, there is incorrect error handling that checks iffetch_message()returnsNone, but this method never returnsNone- it raises exceptions instead.Location
File:
react_roles/react_roles.pyLines: 55-56
Current Code
The Problem
Discord.py's
fetch_message()has two possible outcomes:discord.Messageobject (neverNone)discord.NotFoundexceptionThe current code assumes it can return
None, which is incorrect. This means:if not message:check never triggersfetch_message()will raisediscord.NotFoundinsteadFrom Discord.py Documentation
Notice there's no mention of returning
None- it always returnsMessageor raises an exception.Impact
discord.NotFoundcan propagateFix Required
Replace the None check with proper exception handling:
Similar Issues to Check
Search for other instances of this pattern:
Also check:
fetch_user()- also raises exceptions, never returns Nonefetch_channel()- same behaviorfetch_guild()- same behaviorget_message()(cache lookup) - this one DOES return NonePriority
MEDIUM - Causes incorrect behavior and config pollution, but doesn't always crash.
Testing
After fixing:
Additional Note
For clarity, use
get_message()when you want to check the cache without fetching:Related
This is part of a comprehensive code review. See other issues for additional improvements.
References