G7.6: fix the alert path — urllib UA was rejected 403 by Resend
Caught by TESTING the alert path instead of assuming it. Without an explicit User-Agent, urllib sends 'Python-urllib/3.x' and Resend rejects it 403, while the identical request via curl succeeds. The failure mode this avoids is the worst one a canary has: it would have detected every outage correctly and told nobody. Same bot-filtering trap as the Gitea migrate call earlier today — worth recognising on sight. Also prints the HTTP body on failure. '403 Forbidden' alone sends you hunting for a bad key; the body names the real cause. Verified: alert sent (200). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -177,12 +177,24 @@ def send_alert(subject: str, lines: list[str]) -> bool:
|
||||
headers={
|
||||
"Authorization": f"Bearer {RESEND_KEY}",
|
||||
"Content-Type": "application/json",
|
||||
# ⚠️ REQUIRED. Without an explicit User-Agent, urllib sends
|
||||
# "Python-urllib/3.x" and the request is rejected 403 by bot
|
||||
# filtering — while the identical request via curl succeeds. This
|
||||
# exact failure was caught by testing the alert path rather than
|
||||
# assuming it: the canary would have detected every outage
|
||||
# correctly and told nobody.
|
||||
"User-Agent": "windy-git-canary/1.0",
|
||||
},
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
print(f" alert sent ({r.status})")
|
||||
return True
|
||||
except urllib.error.HTTPError as e:
|
||||
# Print the body. "403 Forbidden" alone sends you hunting for a bad key;
|
||||
# the body usually names the real cause.
|
||||
print(f"!! alert FAILED: HTTP {e.code}: {e.read().decode()[:200]}")
|
||||
return False
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"!! alert FAILED: {e}")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user