Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9bee898
test: :test_tube: add end to end test case to prove issue with not aw…
gingerbenw Apr 17, 2026
4888cac
update test case
gingerbenw Apr 17, 2026
a96aece
reinstate post test
gingerbenw Apr 20, 2026
f037384
test: :test_tube: add health check to hono tests to ensure service do…
gingerbenw Apr 20, 2026
2e1d4e4
add test for post request
gingerbenw Apr 20, 2026
204231b
await next
gingerbenw Apr 20, 2026
50b919e
handle non-error
gingerbenw Apr 21, 2026
553eade
update assertions
gingerbenw Apr 21, 2026
aceb212
update error message assertion
gingerbenw Apr 21, 2026
6c2ce64
pass form data to endpoint and access bugsnag from context
gingerbenw Apr 22, 2026
71550b2
try using a custom step to post json data
gingerbenw Apr 22, 2026
a2ba738
allow multiple content types to POST request assertions
gingerbenw Apr 22, 2026
eb28535
fix definition
gingerbenw Apr 22, 2026
2a5b89b
rethrow caught errors when awaiting next
gingerbenw Apr 22, 2026
521b9db
update aws-lambda hono assertions
gingerbenw Apr 23, 2026
5408358
test: upload test failures
gingerbenw Apr 24, 2026
2c37bcd
assert on both errors and move to unhandled feature
gingerbenw Apr 24, 2026
cef5117
rethrow error
gingerbenw Apr 24, 2026
fbc3cf4
test: update Scenario Ouline: to Scenario when no Examples are present
gingerbenw Apr 24, 2026
f3c97a7
update step description
gingerbenw Apr 24, 2026
0dacde8
test: pin upload-artifact action to latest sha
gingerbenw Apr 24, 2026
2d13751
docs: :bulb: update comment wording
gingerbenw Apr 24, 2026
daddba8
remove unnecessary SIGTERM methods
gingerbenw Apr 24, 2026
b092197
remove unnecessary port exposition
gingerbenw Apr 24, 2026
e69ccea
docs: :memo: add changelog entry
gingerbenw Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/aws-lambda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ jobs:
cd test/aws-lambda
bundle install
bundle exec maze-runner
- name: Upload test artifacts
if: failure()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: aws-lambda-test-results
path: test/aws-lambda/maze_output/
if-no-files-found: ignore
Comment on lines +42 to +48
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

only upload artifacts when the tests fail so we can view the actual payloads

6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [Unreleased]

### Fixed

- (plugin-hono) Fix issue where error handler middleware did not `await next()` [#2735](https://github.com/bugsnag/bugsnag-js/pull/2735)

## [8.9.0] - 2026-04-08

### Fixed
Expand Down
39 changes: 26 additions & 13 deletions packages/plugin-hono/src/hono.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,36 @@ module.exports = {
})

const errorHandler = createMiddleware(async (c, next) => {
next()
let rethrow = false

if (!c.error || !client._config.autoDetectErrors) return
try {
// Catch all thrown values from routes by awaiting next() inside a try/catch block.
// This also ensures non-Error throws are attached to the context and processed without causing the route to hang.
await next()
} catch (err) {
c.error = err
rethrow = true
}

const event = client.Event.create(c.error, false, handledState, 'hono middleware', 1)
if (!c.error) return

if (c.bugsnag) {
c.bugsnag._notify(event)
} else {
client._logger.warn(
'c.bugsnag is not defined. Make sure the @bugsnag/plugin-hono requestHandler middleware is added first.'
)
const { metadata, request } = await getRequestAndMetadataFromReq(c)
event.request = { ...event.request, ...request }
event.addMetadata('request', metadata)
client._notify(event)
if (client._config.autoDetectErrors) {
const event = client.Event.create(c.error, false, handledState, 'hono middleware', 1)

if (c.bugsnag) {
c.bugsnag._notify(event)
} else {
client._logger.warn(
'c.bugsnag is not defined. Make sure the @bugsnag/plugin-hono requestHandler middleware is added first.'
)
const { metadata, request } = await getRequestAndMetadataFromReq(c)
event.request = { ...event.request, ...request }
event.addMetadata('request', metadata)
client._notify(event)
}
}

if (rethrow) throw c.error
})

return { requestHandler, errorHandler }
Expand Down
29 changes: 0 additions & 29 deletions test/aws-lambda/features/promise-rejection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -111,32 +111,3 @@ Scenario: promise rejections are reported when using hono
Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier
And the session "id" is not null
And the session "startedAt" is a timestamp

@hono-app
Scenario Outline: thrown non-error exceptions are reported when using hono
Given I setup the environment
When I invoke the "HonoFunction" lambda in "features/fixtures/hono-app" with the "events/throw-non-error.json" event
Then the lambda response "errorMessage" equals "1"
And the lambda response "errorType" equals "Runtime.UnhandledPromiseRejection"
And the lambda response "trace" is an array with 4 elements
And the lambda response "trace.0" equals "Runtime.UnhandledPromiseRejection: 1"
And the lambda response "body" is null
And the lambda response "statusCode" is null
And the SAM exit code equals 0
When I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
And the event "severity" equals "error"
And the event "severityReason.type" equals "unhandledPromiseRejection"
And the exception "errorClass" equals "InvalidError"
And the exception "message" matches "unhandledRejection handler received a non-error\."
And the exception "type" equals "nodejs"
And the event "metaData.AWS Lambda context.functionName" equals "HonoFunction"
And the event "metaData.AWS Lambda context.awsRequestId" is not null
And the event "device.runtimeVersions.node" matches "^18\.\d+\.\d+$"
When I wait to receive a session
Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier
And the session "id" is not null
And the session "startedAt" is a timestamp
And the event "session.events.handled" equals 0
And the event "session.events.unhandled" equals 1
45 changes: 43 additions & 2 deletions test/aws-lambda/features/unhandled.feature
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Scenario: unhandled asynchronous exceptions are reported when using serverless-e
And the event "session.events.unhandled" equals 1

@hono-app
Scenario Outline: unhandled exceptions are reported when using hono
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These have no Examples to step through, so should use Scenario rather than Scenario Outline

Scenario: unhandled exceptions are reported when using hono
Given I setup the environment
When I invoke the "HonoFunction" lambda in "features/fixtures/hono-app" with the "events/unhandled.json" event
And the SAM exit code equals 0
Expand All @@ -156,7 +156,7 @@ Scenario Outline: unhandled exceptions are reported when using hono
And the event "session.events.unhandled" equals 1

@hono-app
Scenario Outline: unhandled asynchronous exceptions are reported when using hono
Scenario: unhandled asynchronous exceptions are reported when using hono
Given I setup the environment
When I invoke the "HonoFunction" lambda in "features/fixtures/hono-app" with the "events/unhandled-async.json" event
And the SAM exit code equals 0
Expand All @@ -179,3 +179,44 @@ Scenario Outline: unhandled asynchronous exceptions are reported when using hono
And the event "session.events.handled" equals 0
And the event "session.events.unhandled" equals 1

@hono-app
Scenario: thrown non-error exceptions are reported when using hono
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to unhandled.feature as this should have nothing to do with promise rejections

Given I setup the environment
When I invoke the "HonoFunction" lambda in "features/fixtures/hono-app" with the "events/throw-non-error.json" event
Then the lambda response "errorMessage" equals "1"
And the lambda response "errorType" equals "number"
And the lambda response "body" is null
And the lambda response "statusCode" is null
And the SAM exit code equals 0
When I wait to receive 2 errors

Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
And the event "severity" equals "error"
And the event "severityReason.type" equals "unhandledException"
And the exception "errorClass" equals "Error"
And the exception "message" equals "1"
And the exception "type" equals "nodejs"
And the event "metaData.AWS Lambda context.functionName" equals "HonoFunction"
And the event "metaData.AWS Lambda context.awsRequestId" is not null
And the event "device.runtimeVersions.node" matches "^18\.\d+\.\d+$"

# Error thrown by hono
And I discard the oldest error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
And the event "severity" equals "error"
And the event "severityReason.type" equals "unhandledErrorMiddleware"
And the exception "errorClass" equals "InvalidError"
And the exception "message" matches "hono middleware received a non-error\."
And the exception "type" equals "nodejs"
And the event "metaData.AWS Lambda context.functionName" equals "HonoFunction"
And the event "metaData.AWS Lambda context.awsRequestId" is not null
And the event "device.runtimeVersions.node" matches "^18\.\d+\.\d+$"

When I wait to receive a session
Then the session is valid for the session reporting API version "1" for the "Bugsnag Node" notifier
And the session "id" is not null
And the session "startedAt" is a timestamp
And the event "session.events.handled" equals 0
And the event "session.events.unhandled" equals 1
4 changes: 2 additions & 2 deletions test/node/features/express.feature
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Scenario: an unhandled promise rejection in an async callback (without request c
And the exception "message" equals "unhandled rejection in async callback"

Scenario: adding body to request metadata
When I POST the data "data=in_request_body" to the URL "http://express/bodytest"
When I POST the data "data=in_request_body" to the URL "http://express/bodytest" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
Expand Down Expand Up @@ -215,7 +215,7 @@ Scenario: Context-aware console breadcrumbs
And the event "request.clientIp" is not null

Scenario: context loss
When I POST the data "some=body_data" to the URL "http://express/context-loss"
When I POST the data "some=body_data" to the URL "http://express/context-loss" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the exception "errorClass" equals "Error"
Expand Down
2 changes: 1 addition & 1 deletion test/node/features/express_disabled.feature
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ Scenario: a handled error passed to req.bugsnag.notify()
And the event "request.clientIp" is not null

Scenario: adding body to request metadata
When I POST the data "data=in_request_body" to the URL "http://express-disabled/bodytest"
When I POST the data "data=in_request_body" to the URL "http://express-disabled/bodytest" with the content type "application/x-www-form-urlencoded"
And I should receive no errors
12 changes: 6 additions & 6 deletions test/node/features/feature_flags.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Background:
Scenario: adding feature flags for an unhandled error
Given I start the service "express"
And I wait for the host "express" to open port "80"
When I POST the data "a=1&b=2&c=3&d=4" to the URL "http://express/features/unhandled"
When I POST the data "a=1&b=2&c=3&d=4" to the URL "http://express/features/unhandled" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
Expand All @@ -29,7 +29,7 @@ Scenario: adding feature flags for an unhandled error
| d | 4 |
# ensure each request can have its own set of feature flags
When I discard the oldest error
And I POST the data "x=9&y=8&z=7" to the URL "http://express/features/unhandled"
And I POST the data "x=9&y=8&z=7" to the URL "http://express/features/unhandled" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
And the event contains the following feature flags:
| featureFlag | variant |
Expand All @@ -42,7 +42,7 @@ Scenario: adding feature flags for an unhandled error
Scenario: adding feature flags for a handled error
Given I start the service "express"
And I wait for the host "express" to open port "80"
When I POST the data "a=1&b=2&c=3&d=4" to the URL "http://express/features/handled"
When I POST the data "a=1&b=2&c=3&d=4" to the URL "http://express/features/handled" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is false
Expand All @@ -62,7 +62,7 @@ Scenario: adding feature flags for a handled error
| d | 4 |
# ensure each request can have its own set of feature flags
When I discard the oldest error
And I POST the data "x=9&y=8&z=7" to the URL "http://express/features/handled"
And I POST the data "x=9&y=8&z=7" to the URL "http://express/features/handled" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
And the event contains the following feature flags:
| featureFlag | variant |
Expand All @@ -75,7 +75,7 @@ Scenario: adding feature flags for a handled error
Scenario: clearing all feature flags doesn't affect subsequent requests
Given I start the service "express"
And I wait for the host "express" to open port "80"
When I POST the data "a=1&b=2&c=3&d=4&clearAllFeatureFlags" to the URL "http://express/features/unhandled"
When I POST the data "a=1&b=2&c=3&d=4&clearAllFeatureFlags" to the URL "http://express/features/unhandled" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
Expand All @@ -87,7 +87,7 @@ Scenario: clearing all feature flags doesn't affect subsequent requests
And the event "request.httpMethod" equals "POST"
And the event has no feature flags
When I discard the oldest error
And I POST the data "x=9&y=8&z=7" to the URL "http://express/features/unhandled"
And I POST the data "x=9&y=8&z=7" to the URL "http://express/features/unhandled" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
And the event contains the following feature flags:
| featureFlag | variant |
Expand Down
13 changes: 10 additions & 3 deletions test/node/features/fixtures/hono/scenarios/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ const app = new Hono();
const middleware = Bugsnag.getPlugin('hono')

app.use(middleware.requestHandler)

app.use(middleware.errorHandler)

app.get('/', (c) => {
return c.text('Hello from Hono!')
})

app.get('/handled', async (c, next) => {
Bugsnag.notify(new Error('handled'));
c.bugsnag.notify(new Error('handled'));
Copy link
Copy Markdown
Member Author

@gingerbenw gingerbenw Apr 24, 2026

Choose a reason for hiding this comment

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

use the request scoped version of bugsnag

Bad assumption here! the errorHandler middleware will use the cloned client from the requestHandler middleware if it's present, regardless of whether we use Bugsnag or c.bugsnag. this change is unnecessary. (but still perfectly valid)

await next();
});

app.get('/sync', (c) => {
throw new Error('sync')
})

// Causes the app to crash
app.get('/async', (c) => {
setTimeout(function () {
throw new Error('async')
Expand All @@ -48,10 +48,17 @@ app.get('/rejection-async', (c) => {
}, 100)
})

app.get('/throw-non-error', async (c, next) => {
app.get('/throw-non-error', async (c) => {
throw 1
})

// Causes a 'Context is not finalized' Error if the error handler middleware does not `await next()`
app.post('/post-body', async (c) => {
await c.req.raw.json();
c.bugsnag.notify(new Error('error in post body route'));
return c.json({ a: 'test' });
});

serve({
fetch: app.fetch,
port: 80
Expand Down
20 changes: 17 additions & 3 deletions test/node/features/hono.feature
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Scenario: a synchronous thrown error in a route
And the event "metaData.request.query.a" equals "1"
And the event "metaData.request.query.b" equals "2c"

# This route will cause the app to crash
Scenario: an asynchronous thrown error in a route
Then I open the URL "http://hono/async" tolerating any error
And I wait to receive an error
Expand Down Expand Up @@ -90,9 +91,22 @@ Scenario: throwing non-Error error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
And the event "severity" equals "error"
And the event "severityReason.type" equals "unhandledPromiseRejection"
And the event "severityReason.type" equals "unhandledErrorMiddleware"
And the exception "errorClass" equals "InvalidError"
And the exception "message" matches "unhandledRejection handler received a non-error\."
And the exception "message" matches "hono middleware received a non-error\."
And the exception "type" equals "nodejs"
And the event "request.url" equals "http://hono/throw-non-error"
And the event "request.httpMethod" equals "GET"
And the event "request.httpMethod" equals "GET"

Scenario: error handler awaits next()
When I POST the data "{\"a\":1,\"b\":2}" to the URL "http://hono/post-body" with the content type "application/json"
Then I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is false
And the event "severity" equals "warning"
And the exception "errorClass" equals "Error"
And the exception "message" matches "error in post body route"
And the exception "type" equals "nodejs"
And the "file" of stack frame 0 equals "scenarios/app.js"
And the event "request.url" equals "http://hono/post-body"
And the event "request.httpMethod" equals "POST"
2 changes: 1 addition & 1 deletion test/node/features/koa.feature
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Scenario: A handled error with ctx.bugsnag.notify()
And the event "metaData.error_handler.after" is null

Scenario: adding body to request metadata
When I POST the data "data=in_request_body" to the URL "http://koa/bodytest"
When I POST the data "data=in_request_body" to the URL "http://koa/bodytest" with the content type "application/x-www-form-urlencoded"
And I wait to receive an error
Then the error is valid for the error reporting API version "4" for the "Bugsnag Node" notifier
And the event "unhandled" is true
Expand Down
2 changes: 1 addition & 1 deletion test/node/features/koa_disabled.feature
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,5 @@ Scenario: A handled error with ctx.bugsnag.notify()
And the event "request.clientIp" is not null

Scenario: adding body to request metadata
When I POST the data "data=in_request_body" to the URL "http://koa-disabled/bodytest"
When I POST the data "data=in_request_body" to the URL "http://koa-disabled/bodytest" with the content type "application/x-www-form-urlencoded"
And I should receive no errors
9 changes: 5 additions & 4 deletions test/node/features/steps/server_fixture_request_steps.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
require 'net/http'

# Attempts to POST a string of urlencoded data to a server.
# Attempts to POST a string of request body data to a server.
#
# @step_input reqbody [String] urlencoded data to send.
# @step_input reqbody [String] body data to send.
# @step_input url [String] The URL to post data to.
When("I POST the data {string} to the URL {string}") do |reqbody, url|
# @step_input content_type [String] The content type of the data being sent.
When("I POST the data {string} to the URL {string} with the content type {string}") do |reqbody, url, content_type|
Net::HTTP.post(URI(url),
reqbody,
'Content-Type' => 'application/x-www-form-urlencoded')
'Content-Type' => content_type)
end

When('I open the URL {string} tolerating any error') do |url|
Expand Down
Loading