From f3d2561bf926e4fd10fff6aec83dc0246bafac85 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Mon, 29 Jun 2026 17:09:30 -0400 Subject: [PATCH] fix: retry any 429, not just those with a parseable retry_after (v0.1.3) treat every status==429 as retryable: sleep only when retry_after parses, but raise _Retryable either way so aretry's backoff + max_retries cap engages. previously a 429 with no body retry_after and no Retry-After header (edge/Cloudflare/generic webhook) returned a terminal ok=False with no retry, contradicting the documented retry-on-429. Signed-off-by: disqualifier --- README.md | 4 ++-- pyproject.toml | 2 +- src/aiowebhooks/__init__.py | 2 +- src/aiowebhooks/sender.py | 14 ++++++++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 33e0c43..238a6fa 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ send to the core — inheriting rotation, proxy, retry, and result for free. ## Install ``` -aiowebhooks @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiowebhooks.git@v0.1.2 +aiowebhooks @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiowebhooks.git@v0.1.3 # discord embeds / identity helpers need the extra: -aiowebhooks[discord] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiowebhooks.git@v0.1.2 +aiowebhooks[discord] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aiowebhooks.git@v0.1.3 ``` The base pulls `aiohttp` and `commons` (for the retry/backoff engine). Only diff --git a/pyproject.toml b/pyproject.toml index 7f4b64a..ff1dedf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "aiowebhooks" -version = "0.1.2" +version = "0.1.3" description = "async webhook sender (aiohttp) with round-robin urls, retry, and proxy rotation; optional discord.py embeds" requires-python = ">=3.10" dependencies = [ diff --git a/src/aiowebhooks/__init__.py b/src/aiowebhooks/__init__.py index c212c69..623b77e 100644 --- a/src/aiowebhooks/__init__.py +++ b/src/aiowebhooks/__init__.py @@ -21,4 +21,4 @@ from .sender import Webhook __all__ = ["Webhook", "WebhookResult", "WebhookError", "NoUrlsError"] -__version__ = "0.1.2" +__version__ = "0.1.3" diff --git a/src/aiowebhooks/sender.py b/src/aiowebhooks/sender.py index ee8f81b..fb3ebd6 100644 --- a/src/aiowebhooks/sender.py +++ b/src/aiowebhooks/sender.py @@ -201,10 +201,16 @@ class Webhook: error=f"http {status}", response=body, proxy=last_proxy, ) - wait = self._retry_after(status, resp.headers, body) - if wait is not None: - log.warning("webhook 429 on %s; honoring retry_after %.3fs", url, wait) - await asyncio.sleep(wait) + if status == 429: + # every 429 is retryable; honor an explicit retry_after by + # sleeping it, but a 429 with no parseable wait (edge/Cloudflare/ + # generic webhook) still retries under aretry's backoff + cap + wait = self._retry_after(status, resp.headers, body) + if wait is not None: + log.warning("webhook 429 on %s; honoring retry_after %.3fs", url, wait) + await asyncio.sleep(wait) + else: + log.warning("webhook 429 on %s; no retry_after, backing off", url) raise _Retryable(result) if status >= 500: raise _Retryable(result)