fix(relay): bounded recursion in sendTelegram on HTTP 429#3245
fix(relay): bounded recursion in sendTelegram on HTTP 429#3245fuleinist wants to merge 3 commits intokoala73:mainfrom
Conversation
…la73#3060) Add _retryCount parameter (default 0) to sendTelegram(). Guard now bails with console.warn when _retryCount >= 1 on HTTP 429, preventing unbounded recursion when Telegram rate limits persist. Recursive call passes _retryCount + 1. Fixes stack overflow risk under sustained rate limiting. Also add convex/alertRules.ts server-side guard: reject quietHoursStart === quietHoursEnd as an invalid config (treated as always-on without this check).
|
@fuleinist is attempting to deploy a commit to the World Monitor Team on Vercel. A member of the Team first needs to authorize it. |
Verification — Fixed in commit 5f2f223
|
Greptile SummaryThis PR fixes unbounded recursion in
Confidence Score: 4/5Safe to merge after resolving the over-strict quiet-hours validation in The relay fix is correct and clean. The Convex change introduces a P1 inconsistency: the new early guard rejects convex/alertRules.ts — the new Important Files Changed
Reviews (1): Last reviewed commit: "fix(relay): bounded recursion in sendTel..." | Re-trigger Greptile |
| if (args.quietHoursStart !== undefined && args.quietHoursEnd !== undefined && args.quietHoursStart === args.quietHoursEnd) { | ||
| throw new ConvexError("quietHoursStart and quietHoursEnd cannot be equal — set the same value for both means quiet hours are always active; use the enabled flag instead"); | ||
| } |
There was a problem hiding this comment.
New check is stricter than the existing handler-level guard
The added check in validateQuietHoursArgs (line 184) rejects quietHoursStart === quietHoursEnd unconditionally, but the existing check at lines 211–219 only enforces this when effectiveEnabled is true. A user who wants to pre-save equal start/end values while quiet hours are disabled (e.g. configuring defaults before enabling) will now receive an error from the args validator, whereas the handler-level guard would have correctly allowed it. The two checks are inconsistent in scope.
The error message also contains a grammatical fragment: "set the same value for both means quiet hours are always active" should read "setting the same value for both means…".
|
Thanks for chasing this — the core finding is correct and well-diagnosed. The issue is scope: this PR bundles a second, unrelated change — the And the bundled Convex change has a real regression worth flagging before it lands anywhere:
The new check rejects const effectiveEnabled = args.quietHoursEnabled ?? existing?.quietHoursEnabled ?? false;
if (effectiveEnabled) {
// …only here do we enforce start !== end…
}The arg validator runs first, so a user pre-saving defaults with Two options:
Also: small typo in the error string — One more ask for the relay fix itself: a regression test would lock it in. Mock TL;DR:
Vercel "failure" is just the external-contributor preview-deploy auth check; ignore it. |
Reviewer comments from PR koala73#3245: 1. FIX: validateQuietHoursArgs regression (convex/alertRules.ts) The unconditional start===end check rejected quietHoursEnabled=false with start/end defaults. Guard on args.quietHoursEnabled to match the pre-existing handler-level guard. 2. FIX: typo in error string 'set the same value' → 'setting the same value' 3. TEST: bounded 429 retry regression test (notification-relay-429-retry.test.mjs) Confirms sendTelegram returns false on two consecutive 429s without stack-overflow.
Review Updates AppliedAll three reviewer comments from #3245 are now addressed: 1. Quiet-hours regression fix (
|
Bug
In
scripts/notification-relay.cjs:286-291,sendTelegramcalls itself on HTTP 429 with no retry counter:```js
if (res.status === 429) {
const body = await res.json().catch(() => ({}));
const wait = ((body.parameters?.retry_after ?? 5) + 1) * 1000;
await new Promise(r => setTimeout(r, wait));
return sendTelegram(userId, chatId, text); // single retry — but no guard
}
```
The comment says "single retry" but there's no depth/counter parameter to enforce it. If Telegram keeps returning 429 (sustained rate limiting during alert bursts), this recurses indefinitely — causing stack overflow and crash on Railway.
Fix
Also adds server-side guard in `convex/alertRules.ts`: reject `quietHoursStart === quietHoursEnd` as invalid when `quietHoursEnabled=true` (treated as always-on without this check).
Verification Steps
Fixes #3060