Skip to content

fix(hono): Distinguish .use() middleware in sub-apps from .all() handlers#20554

Merged
s1gr1d merged 5 commits intodevelopfrom
sig/hono-route-pattern-tests
Apr 29, 2026
Merged

fix(hono): Distinguish .use() middleware in sub-apps from .all() handlers#20554
s1gr1d merged 5 commits intodevelopfrom
sig/hono-route-pattern-tests

Conversation

@s1gr1d
Copy link
Copy Markdown
Member

@s1gr1d s1gr1d commented Apr 28, 2026

Previously, Hono .route() was patched by checking for an 'ALL' handler (as those are used as middleware) to detect middleware in Hono sub-apps. But this incorrectly treated .all() final handlers as middleware. This broke error capture for .all() error routes (the wrapped handler caught and marked the error, preventing responseHandler from capturing it).

Now we use an arity heuristic (arity = number of params): middleware accepts (context, next), while route handlers accept only (context).

Also added more middleware-related E2E test scenarios and route-patterns tests. Basically adding test scenarios from the Node integration test to the E2E test: https://github.com/getsentry/sentry-javascript/blob/5d0d14531511fcd703438e072723ef98cd700ea3/dev-packages/node-integration-tests/suites/tracing/hono/test.ts

Part of Project: #15260
Builds onto: #20449

@s1gr1d s1gr1d changed the title fix(hono): Distinguish .use() middleware from .all() handlers fix(hono): Distinguish .use() middleware in sub-apps from .all() handlers Apr 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 26.16 kB - -
@sentry/browser - with treeshaking flags 24.63 kB - -
@sentry/browser (incl. Tracing) 44.11 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 46.16 kB - -
@sentry/browser (incl. Tracing, Profiling) 49.06 kB - -
@sentry/browser (incl. Tracing, Replay) 83.46 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 72.94 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 88.14 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 100.79 kB - -
@sentry/browser (incl. Feedback) 43.4 kB - -
@sentry/browser (incl. sendFeedback) 30.96 kB - -
@sentry/browser (incl. FeedbackAsync) 36.14 kB - -
@sentry/browser (incl. Metrics) 27.44 kB - -
@sentry/browser (incl. Logs) 27.59 kB - -
@sentry/browser (incl. Metrics & Logs) 28.28 kB - -
@sentry/react 27.9 kB - -
@sentry/react (incl. Tracing) 46.35 kB - -
@sentry/vue 31.03 kB - -
@sentry/vue (incl. Tracing) 45.95 kB - -
@sentry/svelte 26.18 kB - -
CDN Bundle 28.84 kB - -
CDN Bundle (incl. Tracing) 46.71 kB - -
CDN Bundle (incl. Logs, Metrics) 30.25 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 47.8 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 69.34 kB - -
CDN Bundle (incl. Tracing, Replay) 83.88 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 84.94 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 89.69 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 90.78 kB - -
CDN Bundle - uncompressed 84.55 kB - -
CDN Bundle (incl. Tracing) - uncompressed 139.68 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 88.75 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 143.14 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 212.71 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 257.49 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 260.93 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 271.18 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 274.62 kB - -
@sentry/nextjs (client) 48.84 kB - -
@sentry/sveltekit (client) 44.56 kB - -
@sentry/node-core 58.96 kB +0.02% +9 B 🔺
@sentry/node 170.25 kB +0.01% +10 B 🔺
@sentry/node - without tracing 96.82 kB +0.01% +9 B 🔺
@sentry/aws-serverless 113.69 kB +0.03% +33 B 🔺
@sentry/cloudflare (withSentry) - minified 164.13 kB - -
@sentry/cloudflare (withSentry) 415.2 kB - -

View base workflow run

@s1gr1d s1gr1d marked this pull request as draft April 28, 2026 08:28
@s1gr1d s1gr1d marked this pull request as ready for review April 28, 2026 12:03
@s1gr1d s1gr1d requested a review from a team April 28, 2026 12:03
Copy link
Copy Markdown
Member

@chargome chargome left a comment

Choose a reason for hiding this comment

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

Few minor things, but generally LGTM!

Comment thread packages/hono/src/shared/patchRoute.ts Outdated
* (those are the middleware), leaving the final handler unwrapped.
*
* For `method: 'ALL'` handlers that are last-for-group (from `.use()` or `.all()`),
* we use an arity (# of params) heuristic: middleware takes `(context, next)` (length >= 2),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fancy words in here lol

getIsolationScope().setTransactionName(`${context.req.method} ${routePath(context)}`);

if (context.error) {
if (context.error && !isAlreadyCaptured(context.error)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe we can skip the check here as this is already handled in captureException

function wrapSubAppMiddleware(routes: HonoRoute[]): void {
const lastIndexByKey = new Map<string, number>();
for (const [i, route] of routes.entries()) {
lastIndexByKey.set(`${route.method}\0${route.path}`, i);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe add a comment why we need /0 in here

s1gr1d added 5 commits April 29, 2026 10:45
….all() handlers

Hono registers both .use() middleware and .all() route handlers with
method:'ALL' internally. Previously patchRoute wrapped all method:'ALL'
handlers as middleware spans, incorrectly tracing .all() final handlers.

Use handler.length >= 2 (accepts context + next) to identify middleware
vs handler.length < 2 (accepts only context) for route handlers.

Also guard responseHandler against re-capturing errors already captured
by wrapMiddlewareWithSpan via isAlreadyCaptured check.

Made-with: Cursor
…atterns

Add comprehensive test coverage for the Hono middleware instrumentation:

- E2E route-patterns tests for HTTP methods, registration styles, error capture
- E2E inline middleware tests for direct method, .all(), .on() with inline
  and separately registered middleware across all HTTP methods
- E2E negative test asserting .all() handlers don't create middleware spans
- Unit tests for responseHandler error capture and isAlreadyCaptured guard
- Unit tests for patchRoute arity heuristic (inline mw, separate mw, .on())
- Unit tests for sentry middleware without options (external init pattern)

Made-with: Cursor
@s1gr1d s1gr1d force-pushed the sig/hono-route-pattern-tests branch from 1357397 to fb1626e Compare April 29, 2026 10:00
@s1gr1d s1gr1d enabled auto-merge (squash) April 29, 2026 10:07
@s1gr1d s1gr1d merged commit 1166839 into develop Apr 29, 2026
45 checks passed
@s1gr1d s1gr1d deleted the sig/hono-route-pattern-tests branch April 29, 2026 10:16
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.

2 participants