From 131bb89d6efe4678257c45c3daca133fe36e6494 Mon Sep 17 00:00:00 2001 From: Ejiro Asiuwhu Date: Thu, 26 Mar 2026 09:48:12 +0100 Subject: [PATCH] docs: clarify why 429 retries apply to all HTTP methods A 429 guarantees the server did not process the request, so retrying is safe regardless of idempotency. This differs from network error retries which are restricted to idempotent methods. Added inline comments to both sync and async client request loops. --- sdks/implementations/python/src/stack_auth/_client.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sdks/implementations/python/src/stack_auth/_client.py b/sdks/implementations/python/src/stack_auth/_client.py index 6f876bb10..d71243118 100644 --- a/sdks/implementations/python/src/stack_auth/_client.py +++ b/sdks/implementations/python/src/stack_auth/_client.py @@ -172,6 +172,11 @@ class SyncAPIClient(BaseAPIClient[httpx.Client]): actual_status_hdr = resp.headers.get("x-stack-actual-status") actual_status = int(actual_status_hdr) if actual_status_hdr else resp.status_code + # 429 retries apply to ALL methods (including POST/PATCH). + # Unlike network errors, a 429 guarantees the server did NOT + # process the request, so retrying is safe regardless of + # idempotency. This matches the SDK spec and the behavior of + # Stripe, Anthropic, and OpenAI SDKs. if actual_status == 429 and attempt < self.MAX_RETRIES: delay = self._get_retry_delay(attempt, resp) time.sleep(delay) @@ -248,6 +253,11 @@ class AsyncAPIClient(BaseAPIClient[httpx.AsyncClient]): actual_status_hdr = resp.headers.get("x-stack-actual-status") actual_status = int(actual_status_hdr) if actual_status_hdr else resp.status_code + # 429 retries apply to ALL methods (including POST/PATCH). + # Unlike network errors, a 429 guarantees the server did NOT + # process the request, so retrying is safe regardless of + # idempotency. This matches the SDK spec and the behavior of + # Stripe, Anthropic, and OpenAI SDKs. if actual_status == 429 and attempt < self.MAX_RETRIES: delay = self._get_retry_delay(attempt, resp) await asyncio.sleep(delay)