Skip to content

feat(node): Auto-register Koa error handler on app start - #23463

Merged
mydea merged 1 commit into
developfrom
fn/koa-error-handler
Aug 26, 2026
Merged

feat(node): Auto-register Koa error handler on app start#23463
mydea merged 1 commit into
developfrom
fn/koa-error-handler

Conversation

@mydea

@mydea mydea commented Aug 20, 2026

Copy link
Copy Markdown
Member

The Koa error handler is now registered automatically when the app starts, so setupKoaErrorHandler no longer needs to be called. This mirrors the Hapi change and removes the last manual setup step for Koa error capture.

How it works

Koa's Application is an EventEmitter and emits 'error' for every request error that bubbles up unhandled — the same set of errors a top-level try/catch middleware would see, but order-independent. So instead of injecting a try/catch middleware (whose position in the onion stack was load-bearing), we attach a single app.on('error') listener.

Auto-registration rides a new orchestrion channel on Application.prototype.callback. app.listen() always funnels through callback(), so this also covers http.createServer(app.callback()).

Decisions

  • Attach on the channel's end, not start. Koa registers its own default error listener inside callback() — but only if none exist yet (if (!this.listenerCount('error')) this.on('error', this.onerror)). Attaching before that runs would suppress koa's built-in error logging. Acting on end (after the method body) means koa's default listener is already in place and ours is added alongside it.
  • app.on('error') over a first-position try/catch middleware. Event listeners are additive and order-independent, so there is no ordering conflict with a user's own error handling: if a user middleware catches and handles an error, koa never emits and we correctly don't capture; if it's unhandled, our listener and any user app.on('error') both run.
  • setupKoaErrorHandler stays as an idempotent delegate (deprecated) rather than a hard no-op, so a direct call still works without orchestrion (e.g. error capture with tracing disabled). An idempotency marker on the app means auto-registration plus a manual call never stack up duplicate listeners.
  • attachKoaErrorHandler is also deprecated and marked internal — it exists only so the deprecated setupKoaErrorHandler can delegate to it, and should not be called directly.
  • The Koa integration files were collocated into a koa/ folder to match the Hapi layout.

Auto-registration is exercised end-to-end by dropping the manual call from the koa integration-test scenario and the e2e app; a new unit suite covers the attach behaviour (single idempotent listener, guards, and capture mechanism).

A follow-up will do the same for Express.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e8b1047. Configure here.

Comment thread packages/server-utils/src/integrations/koa/index.ts
Comment thread packages/server-utils/src/integrations/koa/koa-error-handler.ts
Comment thread packages/node/src/integrations/tracing/koa.ts
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 28.57 kB - -
@sentry/browser - with treeshaking flags 26.92 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 26.82 kB - -
@sentry/browser (incl. Tracing) 48.59 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 48.6 kB - -
@sentry/browser (incl. Tracing, Profiling) 51.51 kB - -
@sentry/browser (incl. Tracing, Replay) 88.04 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.45 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 92.75 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 105.45 kB - -
@sentry/browser (incl. Feedback) 45.81 kB - -
@sentry/browser (incl. sendFeedback) 33.36 kB - -
@sentry/browser (incl. FeedbackAsync) 38.46 kB - -
@sentry/browser (incl. Metrics) 29.51 kB - -
@sentry/browser (incl. Logs) 29.8 kB - -
@sentry/browser (incl. Metrics & Logs) 30.43 kB - -
@sentry/react 30.31 kB - -
@sentry/react (incl. Tracing) 50.8 kB - -
@sentry/vue 35.69 kB - -
@sentry/vue (incl. Tracing) 50.82 kB - -
@sentry/svelte 28.59 kB - -
CDN Bundle 30.36 kB - -
CDN Bundle (incl. Tracing) 49.06 kB - -
CDN Bundle (incl. Logs, Metrics) 32.56 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 50.94 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 72.98 kB - -
CDN Bundle (incl. Tracing, Replay) 86.56 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.44 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 92.34 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.27 kB - -
CDN Bundle - uncompressed 89.97 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.74 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 96.26 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.43 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.36 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 266.17 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 271.85 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279.87 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.53 kB - -
@sentry/nextjs (client) 53.34 kB - -
@sentry/sveltekit (client) 49.02 kB - -
@sentry/core/server 65.21 kB - -
@sentry/core/browser 52.34 kB - -
@sentry/node 121.87 kB +0.15% +177 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 85.19 kB +0.02% +11 B 🔺
@sentry/node - without tracing 87.72 kB +0.22% +190 B 🔺
@sentry/aws-serverless 96.18 kB +0.24% +229 B 🔺
@sentry/cloudflare (withSentry) - minified 199.48 kB - -
@sentry/cloudflare (withSentry) 495.44 kB - -

View base workflow run

@mydea
mydea marked this pull request as ready for review August 20, 2026 11:16
@mydea
mydea requested review from a team as code owners August 20, 2026 11:16
@mydea
mydea requested review from JPeer264, chargome, isaacs and s1gr1d and removed request for a team August 20, 2026 11:16
@mydea
mydea marked this pull request as draft August 20, 2026 11:33
timestamp: expect.any(Number),
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
},
{

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.

we do not add a middleware ourselves anymore, so this disappears here.

@mydea
mydea marked this pull request as ready for review August 21, 2026 06:48

@JPeer264 JPeer264 left a comment

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.

q: Any reason why we don't remove it already?

@mydea

mydea commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

q: Any reason why we don't remove it already?

my main reasoning was that it is impossible to prepare this in v10, and it likely pretty high-impact (e.g. everyone using koa has this). and the cost of keeping this around in v11 is rather small.

The Koa error handler now registers itself automatically when the app starts, so
`setupKoaErrorHandler` no longer needs to be called. The handler is attached as an
order-independent `app.on('error')` listener via a new orchestrion `callback`
channel; the listener is added after koa registers its own default error listener
so koa's built-in error logging is preserved.

`setupKoaErrorHandler` and the shared `attachKoaErrorHandler` are both deprecated;
`setupKoaErrorHandler` remains an idempotent delegate for backwards compatibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
revert hasspansenabled
fix koa test

fix span attachment to error
@mydea
mydea force-pushed the fn/koa-error-handler branch from 77b29b8 to 40cae03 Compare August 26, 2026 07:35
@mydea
mydea merged commit 683f190 into develop Aug 26, 2026
201 checks passed
@mydea
mydea deleted the fn/koa-error-handler branch August 26, 2026 07:59
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