Skip to content

fix(logging): stop 4xx scanner traffic reading as errors, and mark alertable failures - #369

Merged
important-new merged 1 commit into
InspectorHub:mainfrom
important-new:fix/framework-error-logging
Oct 11, 2026
Merged

important-new merged 1 commit into
InspectorHub:mainfrom
important-new:fix/framework-error-logging

Conversation

@important-new

Copy link
Copy Markdown
Contributor

Summary

React Router's default handleError calls console.error for 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 native error entries 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 through console.info, so the native level was always info regardless of the level inside the JSON line.

What changes

  • handleError export (app/entry.server.tsx, logic in server/lib/route-error-logging.ts): 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 logged once with the method and the path only, never the query string (it can carry one-shot secrets such as reset tokens).
  • Logger console routing: ERROR goes through console.error, WARN through console.warn, everything else through console.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 structured data object, so a code that an operator wants to alert on has to be part of the message.
  • Resend adapter: the failure kind is now part of the log message. A used-up sending quota is logged as [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

Change
Self-host / standalone No functional change. Log volume at error level drops on a public deployment; the native log level is now accurate. Nothing to configure.
Hosted deployments Same. An alert rule can now match [alert:email-quota-exceeded] or filter on the native error level 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.alert was undefined, and the logger sent every level to console.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

  • Type-aware eslint was not run locally (memory ceiling on the author's machine); relying on CI verify.
  • Existing logger.error call sites are unchanged; deciding which of them deserve logger.alert is deliberately left for follow-ups.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FDq9K9niRKMErgty3ca3DD

…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
@important-new
important-new merged commit 5eef03f into InspectorHub:main Oct 11, 2026
20 checks passed
@important-new
important-new deleted the fix/framework-error-logging branch October 11, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant