Skip to content

Conversation

@mcollina
Copy link
Member

Summary

  • Fix synchronous errors thrown in request callbacks being swallowed when underlying stream is destroyed
  • Add proper error handling in RequestHandler.onHeaders() to catch, cleanup, and re-throw errors
  • Ensure errors are propagated through existing error handling chain via Request.onHeaders()

Rationale

Previously, when a synchronous error was thrown in a request callback after the underlying stream was destroyed, the error would be swallowed and become an uncaught exception. This happened because the RequestHandler.onHeaders() method called user callbacks via runInAsyncScope() without wrapping it in a try-catch block.

Changes

Bug Fixes

  • Added try-catch block around callback invocation in RequestHandler.onHeaders()
  • Properly destroy response stream when callback throws synchronously
  • Re-throw error to be handled by existing error handling in Request.onHeaders()
  • Added comprehensive test coverage for synchronous error scenarios

Implementation Details

The fix ensures that:

  1. Synchronous errors in callbacks are caught immediately
  2. Response streams are properly cleaned up before propagating errors
  3. Errors flow through the existing error handling chain (Request.onHeadersthis.abort(err))
  4. No uncaught exceptions occur due to swallowed callback errors

Test Plan

  • Added tests for synchronous errors in successful request callbacks
  • Added tests for immediate errors thrown in callbacks
  • Added tests for error handling in failure scenarios
  • Verified existing tests continue to pass
  • Confirmed no regressions in error handling behavior

🤖 Generated with Claude Code

When a synchronous error is thrown in a request callback after the
underlying stream is destroyed, the error was being swallowed and
becoming an uncaught exception. This fix adds proper error handling
in RequestHandler.onHeaders() to catch these errors, destroy the
response stream cleanly, and re-throw the error to be handled by
the existing error handling chain.

Fixes the case where user code throws synchronously in request
callbacks when streams are already destroyed, ensuring errors
are properly propagated through the abort mechanism.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina mcollina requested review from Uzlopak and metcoder95 August 21, 2025 19:06
@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 21, 2025

@mcollina

Do we need to check all occurences of runInAsyncScope in regard of being wrapped in try catch blocks?

@mcollina
Copy link
Member Author

That might be the case.

Copy link
Member

@metcoder95 metcoder95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we backport it to v6?

Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
@mcollina
Copy link
Member Author

I think this has a bug still, I need to look at it again.

@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 22, 2025

Fixed the linting

…andler

When a synchronous error is thrown in a request callback, it should
propagate to the global uncaughtException handler rather than being
caught and causing the process to abort. This change uses process.nextTick()
to re-throw the error, allowing it to reach the uncaughtException handler
as expected.

- Modified error handling in RequestHandler.onHeaders() to use nextTick
- Updated tests to verify errors reach uncaughtException handler
- Fixed flaky stats test expectation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
@Uzlopak
Copy link
Contributor

Uzlopak commented Aug 25, 2025

@mcollina
Oh you mean, it was totally "swallowed" no even throwing an unhandledPromiseRejection?

@mcollina
Copy link
Member Author

Yes

Copy link
Contributor

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Combine res.on('error', noop) call directly into util.destroy
for cleaner code structure.
Copy link
Contributor

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Collaborator

@Ethan-Arrowood Ethan-Arrowood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests look good. nice fix

@metcoder95 metcoder95 merged commit d0b2899 into main Aug 26, 2025
35 of 36 checks passed
@Uzlopak Uzlopak deleted the fix/catch-sync-errors-in-request-callback branch August 26, 2025 09:28
@github-actions github-actions bot mentioned this pull request Sep 9, 2025
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.

5 participants