Alert fetch silently truncates at the pagination cap, causing mass false all-clears #146
Labels
No labels
area:ai
area:ci-cd
area:notifications
area:observability
area:public-pages
backlog
bug
duplicate
enhancement
help wanted
invalid
question
type:decision
type:feature
type:infra
type:maintenance
type:security
v1.0.1
v1.1.0
v1.2.0
v1.3.0
v2.0.0
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/WeatherBot#146
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
fetch_alerts_for_statesfollowspagination.nextbut stops at a hard cap and still returns a successful result (app/services/nws.py:220):When the cap is hit,
featuresis silently truncated and the function returns a normallist[NWSAlert]— indistinguishable from a complete fetch. Every alert on page 11+ is therefore missing fromactive_matches, and_clear_missing_alertsclears it as "disappeared from the feed", firing all-clears for alerts that are still active.The comment three lines above the loop states this is exactly what pagination was added to prevent:
The cap reintroduces the same failure at page 10 instead of page 1. It is strictly a question of feed size — a big enough multi-state outbreak triggers mass false all-clears, and the larger the outbreak the more likely it is, which is the worst possible correlation for an alerting system.
Fix
Hitting the cap must not look like success. Either:
None, so the caller skips the cycle rather than mass-clearing (consistent with the existing#40skip-on-outage contract), or_clear_missing_alertsskips the "disappeared" sweep for that cycle.Either way, log loudly at WARNING when the cap is reached — today it is invisible. Raising the cap alone is not a fix; it just moves the cliff.
Related
Same family as #145 (false all-clears). #145 is a supersession-detection gap; this is a data-completeness gap. Both end in an unwarranted all-clear.
Fixed in #150 (merged). CI green.
Fetches now return an
AlertFeed— alistsubclass, so every existing caller and test is untouched — carrying acompleteflag set False when pages remained at the cap.process_alertsstill dispatches what it received (those alerts are real, and truncation happens during outbreaks when they matter most) but skips the clearing pass entirely, because absence from a partial feed proves nothing.The cap moved 10 → 50, far above any plausible legitimate feed for the states one install monitors, so it reads as the runaway guard it is rather than an operating limit. Hitting it now logs at ERROR instead of passing silently.
Verified locally in Docker: ruff, 805 SQLite-tier tests, migrations on Postgres 16, 4 Postgres-tier tests. Includes a control test (pagination ending naturally →
complete is True, feed still clears) so the guard cannot silently stop legitimate all-clears.