Repository navigation
fix(logging): stop 4xx scanner traffic reading as errors, and mark alertable failures - #369
Merged
important-new merged 1 commit intoOct 11, 2026
Conversation
…ertable failures Workers Logs reported many native `error` entries that were internet scanners probing paths that do not exist, each logged as a stack trace by React Router's default handleError, which console.errors every error it sees, a 404 or 405 included. Any alert on error-level logs would fire constantly. - route-error-logging: export handleError from entry.server. A 4xx route error, a 4xx Response and a request the client already aborted are not logged; a 5xx or any thrown non-response error is, with method and path only (never the query string, which can carry one-shot secrets). - logger: write ERROR through console.error and WARN through console.warn. The console method sets the native level in Workers Logs; everything used to go through console.info, so the native level was always info. - logger.alert(code, message): an ERROR whose message starts with [alert:<code>]. Log alert rules can match message text but not the data object, so the code has to live in the message. - Resend adapter: the failure kind is now part of the log message. A used-up sending quota is an alert (email-quota-exceeded); a suppressed recipient is expected and logs as a warning; any other failure stays an error. New specs were written red first: the route-error module did not exist, logger.alert was undefined, and the logger sent every level to console.info. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FDq9K9niRKMErgty3ca3DD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
React Router's default
handleErrorcallsconsole.errorfor every error it sees, including the 404 it raises for an unmatched route and the 405 for a POST to a page. Internet scanners probe paths that do not exist all day, so on a public deployment Workers Logs fills with nativeerrorentries that are only stack traces of expected 4xx answers. Any alert or filter on error-level logs then fires constantly. The logger also wrote every level throughconsole.info, so the native level was alwaysinforegardless of the level inside the JSON line.What changes
handleErrorexport (app/entry.server.tsx, logic inserver/lib/route-error-logging.ts): a 4xx route error, a 4xxResponseand a request the client already aborted are not logged. A 5xx, or any thrown non-response error, is logged once with the method and the path only, never the query string (it can carry one-shot secrets such as reset tokens).console.error, WARN throughconsole.warn, everything else throughconsole.info. The line is still one JSON object with the same fields.logger.alert(code, message, data?): an ERROR whose message starts with[alert:<code>]. Workers Logs alert rules can match message text but not the structureddataobject, so a code that an operator wants to alert on has to be part of the message.[alert:email-quota-exceeded]; a suppressed recipient is expected and logs as a warning; any other failure stays an error, now with the kind in its message. Return values are unchanged.Impact
[alert:email-quota-exceeded]or filter on the nativeerrorlevel without scanner noise.No migrations, no new bindings, no new environment variables, no API or return-value changes.
Tests
Written red first: the route-error module did not exist,
logger.alertwas undefined, and the logger sent every level toconsole.info. New specs cover the 4xx and 5xx and aborted-request decisions, that the query string never reaches the log, the console routing, the alert marker, and the three Resend failure outcomes. Full unit (1090 files) and web (484 files) suites pass under UTC.Notes for reviewers
verify.logger.errorcall sites are unchanged; deciding which of them deservelogger.alertis deliberately left for follow-ups.🤖 Generated with Claude Code
https://claude.ai/code/session_01FDq9K9niRKMErgty3ca3DD