Skip to content

fix: trace parameter callbacks and preserve error attribution - #215

Merged
bjohansebas merged 3 commits into
masterfrom
fix/trace-router-params
Oct 23, 2026
Merged

bjohansebas merged 3 commits into
masterfrom
fix/trace-router-params

Conversation

@bjohansebas

Copy link
Copy Markdown
Member

Errors from router.param() callbacks bypassed tracing, so they could go unreported or be attributed to an error handler that only forwarded them. Trace each executed parameter callback and publish its errors once, at their origin. In the example from #213, ctx.handler.name now identifies validateId and ctx.param identifies id.

The changes are split into two commits:

  • Trace parameter callbacks: share tracing helpers with layer handlers and expose handler and param while retaining the associated layer and request-scoped matched route. Preserve parameter caching, callback arguments, routing signals and promise behavior. Skip tracing context allocation when there are no subscribers.
  • Preserve context and attribution: run asynchronous completion through asyncStart.runStores so its bound stores remain active during asyncEnd. Track error identities per request at router boundaries, matching failures and error-handler entry so errors originating before tracing was enabled, or during URL decoding, are not blamed on forwarding handlers. Replacement errors are still reported at their origin.

Update the diagnostics documentation and add 57 regression tests covering parameter callbacks, synchronous and asynchronous errors, concurrent requests, nested router copies, bound stores, late subscriptions and malformed URLs.

Validation on Node 26.10.0:

  • npm test -- --reporter dot — 825 passing.
  • npm run lint — passed.
  • npm run test-types — passed.

Fixes #213.

Parameter callbacks bypassed layer instrumentation, so their errors were
either missing from the error channel or attributed to a downstream error
handler that merely forwarded them.

Trace each executed parameter callback and share error publication and
deduplication with layer handlers. Expose the actual callback as handler
and its parameter name as param while retaining the associated Layer and
request-scoped matched route pattern. This identifies the origin without
modifying shared Layer metadata or Route.dispatch.

Preserve parameter caching, callback arguments, routing signals and promise
semantics. Skip tracing context allocation when there are no subscribers.
Document the context fields and cover synchronous and asynchronous errors,
concurrent requests, nested router copies and bound tracing stores with
24 regression tests.

Refs #213.

Validation: npm test (792 passing), npm run lint, npm run test-types.
Publishing asyncStart directly skipped its bound AsyncLocalStorage stores,
leaving asyncEnd subscribers without the expected context. Errors raised
before tracing was enabled or during route matching could also be blamed
on handlers and mounted routers that merely forwarded them.

Run async completion publication through asyncStart.runStores so bound
stores remain active through asyncEnd without scheduling additional work.
Keep the existing order of error propagation and lifecycle events.

Record error identities at router boundaries, matching failures and error
handler entry. Track them per request even when their origin was untraced,
so a late subscriber cannot misattribute a forwarded error. Newly created
replacement errors are still published at their origin, and the existing
registered symbol preserves deduplication across router copies.

Document these semantics and add 33 tests for bound stores, late
subscriptions, incoming errors and malformed URLs in mounted routers.

Validation: npm test (825 passing), npm run lint, npm run test-types.

@logaretm logaretm 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.

Minor nit

Comment thread lib/tracing.js Outdated
Use one callParam helper for traced and untraced callbacks so exception,
promise rejection and deprecation handling cannot drift between them.
Keep completion notification optional and preserve tracing's end event
in finally. Resolved promises still require an explicit next() call.

Cover resolved promises without tracing and correct the rejection test
to exercise the parameter callback itself.

Validation: 826 tests passing, npm run lint, npm run test-types.
@bjohansebas
bjohansebas merged commit 098ea78 into master Oct 23, 2026
16 checks passed
@bjohansebas
bjohansebas deleted the fix/trace-router-params branch October 23, 2026 18: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.

Error tracing on router.param

2 participants