openai/openai-python · error · InvalidWebhookSignatureError
Webhook timestamp is too old
Error message
Webhook timestamp is too old
What it means
Anti-replay protection in webhook_signature_matches: the signed webhook-timestamp is older than the allowed tolerance (default 5 minutes) relative to server time, so the signature is rejected as a possible replay.
Source
Thrown at src/openai/lib/_webhooks.py:34
*,
secret: str,
tolerance: int,
) -> bool:
"""Validate the replay window and compare the supplied signatures."""
signature_header = get_required_header(headers, "webhook-signature")
timestamp = get_required_header(headers, "webhook-timestamp")
webhook_id = get_required_header(headers, "webhook-id")
# Validate timestamp to prevent replay attacks
try:
timestamp_seconds = int(timestamp)
except ValueError:
raise InvalidWebhookSignatureError("Invalid webhook timestamp format") from None
now = int(time.time())
if now - timestamp_seconds > tolerance:
raise InvalidWebhookSignatureError("Webhook timestamp is too old") from None
if timestamp_seconds > now + tolerance:
raise InvalidWebhookSignatureError("Webhook timestamp is too new") from None
# Extract signatures from v1,<base64> format
# The signature header can have multiple values, separated by spaces.
# Each value is in the format v1,<base64>. We should accept if any match.
signatures: list[str] = []
for part in signature_header.split():
if part.startswith("v1,"):
signatures.append(part[3:])
else:
signatures.append(part)
# Decode the secret if it starts with whsec_
if secret.startswith("whsec_"):
decoded_secret = base64.b64decode(secret[6:])
else:View on GitHub (pinned to 9917c6e28e)
Solutions
- Sync the receiving server's clock (NTP/chrony)
- If you legitimately process delayed deliveries (queues), pass a larger tolerance in seconds to webhook_signature_matches
- Do not re-process captured old requests; fetch fresh state instead
Example fix
# before webhook_signature_matches(body, headers, secret) # after webhook_signature_matches(body, headers, secret, tolerance=3600)
Defensive patterns
Strategy: try-catch
Validate before calling
ts = int(headers["webhook-timestamp"])
import time
if time.time() - ts > tolerance:
# queue-delayed webhook: widen tolerance or fetch fresh state Try / catch
try:
ok = webhook_signature_matches(body, headers, secret, tolerance=3600)
except InvalidWebhookSignatureError as e:
if "too old" in str(e):
return Response(408)
raise Prevention
- Run NTP on webhook receivers
- Size tolerance to your delivery pipeline latency
- Re-fetch state instead of replaying old webhook bodies
When it happens
Trigger: Receiving a webhook more than tolerance seconds after it was signed; also caused by server clock skew, or replaying/queuing old webhook deliveries.
Common situations: Queue backlog delaying delivery; NTP drift on the receiving host; processing retries of old requests; deliberately re-sending captured payloads.
Related errors
- Webhook timestamp is too new
- Invalid webhook timestamp format
- The webhook secret must either be set using the env var, OPE
- The given webhook signature does not match the expected sign
AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28).
Data as JSON: /api/errors/11868d80542ebed6.
Report an issue: GitHub.