SDK Setup
// instrumentation.ts
import * as Sentry from '@sentry/hono/node'
Sentry.init({ dsn: '...', tracesSampleRate: 1.0 })
// api.ts
import { sentry } from '@sentry/hono'
import { Hono } from 'hono'
import { openAPIRouteHandler } from 'hono-openapi'
import { Scalar } from '@scalar/hono-api-reference'
const app = new Hono()
.use(sentry(app as unknown as Hono)) // cast required — TS rejects Hono<CustomEnv>
.basePath('/api')
.use('/*', authMiddleware)
.route('/', someRoute)
.onError(handleError)
app.get('/openapi.json', openAPIRouteHandler(app, openApiConfig))
app.get('/docs', Scalar(scalarConfig))
Steps to Reproduce
- Create a Hono app with
.basePath(), .route(), and .use() registered routes.
- Add
.get() routes for API reference (Scalar or Swagger) and /openapi.json.
- Mount
sentry() as the first middleware (before .basePath()).
- Initialize Sentry via
@sentry/hono/node.
- Visit
/docs or /openapi.json.
Expected Result
/docs renders the Scalar UI with all registered routes and their schemas.
/openapi.json returns the full OpenAPI spec.
Actual Result
/docs renders a blank Scalar shell with no routes listed.
/openapi.json returns an empty or minimal spec (routes missing).
- All
.route()-registered endpoints respond correctly.
- Removing
sentry() from the chain immediately restores correct behavior.
Additional Context
Environment
@sentry/hono: 10.53.1
hono: 4.x
- Node.js: 24.x, ESM, loaded via
node --import=./dist/instrumentation.js
@scalar/hono-api-reference + hono-openapi
- Deployment: Cloud Run
Analysis of applyPatches
sentry() calls applyPatches(app) which applies two patches:
patchAppUse — wraps app.use via Proxy to inject OTel spans into middleware handlers.
installRouteHookOnPrototype — patches HonoBase.prototype.route globally (on the prototype, not the instance) to wrap sub-app middleware.
Neither patch explicitly targets .get(). The Scalar UI and /openapi.json are registered via .get(), so they should be unaffected. Yet removing sentry() is the only change needed to restore them.
Hypotheses
H1 — installRouteHookOnPrototype prototype mutation:
Patching HonoBase.prototype.route globally may corrupt the internal routing state of sub-apps created by .basePath() (which calls #clone()). The cloned app shares the same routes array reference as the parent; prototype-level wrapping may interfere with how Hono resolves .get() routes defined on the parent after a .basePath() chain.
H2 — ESM Symbol identity hazard:
@sentry/hono's requestHandler uses GET_MATCH_RESULT (a Symbol()) from hono/request/constants to access route match data via c.req[GET_MATCH_RESULT]. If the module graph resolves two separate instances of hono (one inside @sentry/hono's own deps, one in the app), the Symbol identities diverge and the lookup silently returns undefined, causing routePath() to produce incorrect route patterns — potentially breaking the route resolution pipeline for .get() routes.
TypeScript cast required
The sentry() function signature expects Hono (no generics), but Hono apps with custom env types (Hono<CtxEnv>) require app as unknown as Hono. This cast suggests the BETA type definitions are incomplete.
Workaround
Remove sentry() from the middleware chain. Errors continue to be captured via the onError handler using captureRequestError from @sentry/node (initialized via @sentry/hono/node).
SDK Setup
Steps to Reproduce
.basePath(),.route(), and.use()registered routes..get()routes for API reference (Scalar or Swagger) and/openapi.json.sentry()as the first middleware (before.basePath()).@sentry/hono/node./docsor/openapi.json.Expected Result
/docsrenders the Scalar UI with all registered routes and their schemas./openapi.jsonreturns the full OpenAPI spec.Actual Result
/docsrenders a blank Scalar shell with no routes listed./openapi.jsonreturns an empty or minimal spec (routes missing)..route()-registered endpoints respond correctly.sentry()from the chain immediately restores correct behavior.Additional Context
Environment
@sentry/hono: 10.53.1hono: 4.xnode --import=./dist/instrumentation.js@scalar/hono-api-reference+hono-openapiAnalysis of
applyPatchessentry()callsapplyPatches(app)which applies two patches:patchAppUse— wrapsapp.usevia Proxy to inject OTel spans into middleware handlers.installRouteHookOnPrototype— patchesHonoBase.prototype.routeglobally (on the prototype, not the instance) to wrap sub-app middleware.Neither patch explicitly targets
.get(). The Scalar UI and/openapi.jsonare registered via.get(), so they should be unaffected. Yet removingsentry()is the only change needed to restore them.Hypotheses
H1 —
installRouteHookOnPrototypeprototype mutation:Patching
HonoBase.prototype.routeglobally may corrupt the internal routing state of sub-apps created by.basePath()(which calls#clone()). The cloned app shares the sameroutesarray reference as the parent; prototype-level wrapping may interfere with how Hono resolves.get()routes defined on the parent after a.basePath()chain.H2 — ESM Symbol identity hazard:
@sentry/hono'srequestHandlerusesGET_MATCH_RESULT(aSymbol()) fromhono/request/constantsto access route match data viac.req[GET_MATCH_RESULT]. If the module graph resolves two separate instances ofhono(one inside@sentry/hono's own deps, one in the app), the Symbol identities diverge and the lookup silently returnsundefined, causingroutePath()to produce incorrect route patterns — potentially breaking the route resolution pipeline for.get()routes.TypeScript cast required
The
sentry()function signature expectsHono(no generics), but Hono apps with custom env types (Hono<CtxEnv>) requireapp as unknown as Hono. This cast suggests the BETA type definitions are incomplete.Workaround
Remove
sentry()from the middleware chain. Errors continue to be captured via theonErrorhandler usingcaptureRequestErrorfrom@sentry/node(initialized via@sentry/hono/node).