fix: reject a non-str stored token as stale before rebuilding the webhook (dpywebhooks-3)

Webhook.partial does no token validation of its own, so a store record with
token=None (e.g. a mongo/file store deserializing a missing field as null)
rebuilt and cached a token-less webhook with no error. Webhook.send then
raised a raw ValueError, not HTTPException, so send()'s dead-webhook except
never fired and the poisoned record was never cleared - the channel got
permanently stuck. Validate the token type before partial() so a bad record
takes the same clear-and-fall-through path as any other stale record.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-06 00:14:56 -04:00
parent b20c5445b4
commit ee3c38972b
+8 -1
View File
@@ -351,11 +351,18 @@ class DPYWebhooks:
async def _restore_from_store(self, channel: discord.TextChannel) -> "Optional[discord.Webhook]":
"""rebuild a partial webhook from the stored record; on a rebuild fault, treat the
record as stale (clear + return None so the caller can recreate)"""
record as stale (clear + return None so the caller can recreate)
Webhook.partial does no token validation of its own and will happily cache a
token-less webhook, which then raises a raw ValueError (not HTTPException) on
send - bypassing the self-heal path entirely and getting stuck forever. reject a
non-str token here, before partial(), so that record is treated as stale too"""
record = await self._store.get(channel.id)
if not record:
return None
try:
if not isinstance(record["token"], str):
raise TypeError(f"stored token must be str, got {type(record['token']).__name__}")
webhook = discord.Webhook.partial(
int(record["webhook_id"]),
record["token"],