Interpret Retry-After with a fixed deadline

AI assisted the preparation of this article, and its Python example was tested locally with synthetic fixtures.
A throttling response can include a Retry-After value, but a delay is not permission to repeat an operation. First decide whether the request is safe to retry; then decide whether the server's requested wait fits the remaining time.
Flow: Response hint, then Parse delay, then Check deadline, then Wait or defer.
Make the delay decision without sleeping
The pure fixture below receives an aware clock and two nonnegative limits in seconds. It accepts ASCII decimal seconds or an HTTP date parsed by Python. A past date yields zero remaining delay. Malformed values and naive dates are rejected rather than interpreted in the machine's local time zone.
from datetime import timezone
from email.utils import parsedate_to_datetime
import math
def decide(header, now, remaining, max_wait):
if now.tzinfo is None or now.utcoffset() is None:
raise ValueError("aware clock required")
if not all(isinstance(x, (int, float)) and math.isfinite(x) and x >= 0
for x in (remaining, max_wait)):
raise ValueError("invalid limits")
if not isinstance(header, str) or not header:
raise ValueError("invalid Retry-After")
if header.isascii() and header.isdigit():
delay = int(header)
else:
try:
date = parsedate_to_datetime(header)
if date.tzinfo is None or date.utcoffset() is None:
raise ValueError("naive date")
delay = max(0, (date.astimezone(timezone.utc) - now.astimezone(timezone.utc)).total_seconds())
except (TypeError, ValueError, OverflowError):
raise ValueError("invalid Retry-After") from None
if delay > remaining or delay > max_wait:
return ("defer", None)
return ("wait", delay)
The caller supplies a fixed test clock, so the fixture needs no sleep. Check a delay in seconds, a future date, a past date, a value exactly at the allowed boundary and a wait that exceeds each limit. A requested wait longer than the available budget yields defer: shortening it and retrying sooner would not follow the stated wait.
The code accepts only ASCII digits for delay-seconds. Unicode digit strings and signed or decimal values are not silently treated as that syntax. Python's date parser accepts more date formats than a complete HTTP validator might; this is a deliberately narrow decision fixture, not a general header conformance implementation.
RFC 9110 defines the delay-seconds and HTTP-date forms. RFC 6585 says a 429 response may include the field. Python's date parsing documentation describes the helper.
Decide whether another attempt is allowed
An unsafe write can have an ambiguous outcome even when the client sees a timeout. A valid delay does not make that write idempotent or prove the provider accepted an idempotency key. Reconcile an uncertain action according to the actual provider contract before deciding to repeat it.
A real scheduler also needs a clock-skew policy, attempt limits, cancellation and an explicit fallback for a missing or invalid header. The fixture raises for invalid input and does not retry, sleep, contact a provider or promise that a future attempt will succeed.