diff --git a/src/dpy_webhooks/dpy_webhooks.py b/src/dpy_webhooks/dpy_webhooks.py index 0ab6d4d..aedbebb 100644 --- a/src/dpy_webhooks/dpy_webhooks.py +++ b/src/dpy_webhooks/dpy_webhooks.py @@ -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"],