Skip to content

Stop flagging JAX-RS/Spring MVC spans as errors when the exception maps to a non-5xx response - #12248

Open
katzj wants to merge 2 commits into
DataDog:masterfrom
katzj:katzj/fix-error-flag-on-mapped-exceptions
Open

Stop flagging JAX-RS/Spring MVC spans as errors when the exception maps to a non-5xx response#12248
katzj wants to merge 2 commits into
DataDog:masterfrom
katzj:katzj/fix-error-flag-on-mapped-exceptions

Conversation

@katzj

@katzj katzj commented Aug 20, 2026

Copy link
Copy Markdown

What Does This Do

  • JAX-RS/Jakarta-RS: when a resource method throws a WebApplicationException (or a
    subclass, e.g. NotFoundException), the jax-rs.request/jakarta-rs.request span's
    error flag is now decided from the status embedded in the exception's Response,
    instead of unconditionally being marked as an error.
  • Spring MVC: same idea for @ResponseStatus, ResponseStatusException, and (Spring 6+)
    ErrorResponse (e.g. NoResourceFoundException) on the spring-web-controller span.
  • In both cases, the status is checked against the same "server error status" set already
    used for the root HTTP span (DD_TRACE_HTTP_SERVER_ERROR_STATUSES), so behavior stays
    consistent with whatever a user has already configured there.
  • Adds an opt-in DD_TRACE_RESPONSE_STATUS_EXCEPTIONS / trace.response-status.exceptions
    config for exceptions that don't follow any of the above conventions - a comma-separated
    list of fully.qualified.ExceptionClass#accessorMethod entries. When a thrown exception
    (or a subclass of one) matches, the named no-arg accessor is invoked reflectively and its
    numeric return value is used the same way. This is opt-in and precise on purpose: it only
    ever reflects on classes/methods a user explicitly named, rather than guessing at common
    accessor names across arbitrary exceptions (which risks silently clearing a genuine error).

Motivation

JAX-RS and Spring MVC commonly signal a non-2xx response by throwing an exception that the
framework's own exception-mapping machinery turns into a normal HTTP response (e.g. a 404).
The tracer was flagging the resource/controller span as an error unconditionally whenever
such an exception was thrown, even though the actual HTTP response was a routine non-5xx -
flooding error tracking with non-actionable "errors" for expected control flow. The root
span was never affected by this (it already reflects the real status code correctly), which
is why this showed up as a healthy root span next to an errored child span.

Solves #7141
Solves #7288

Additional Notes

  • No new default behavior for exceptions that don't carry a recognizable status - they're
    still flagged as errors as before.
  • Public docs for the new DD_TRACE_RESPONSE_STATUS_EXCEPTIONS config will need a follow-up
    update in the Datadog docs site.

Contributor Checklist

@katzj

katzj commented Aug 20, 2026

Copy link
Copy Markdown
Author

Probably should be labeled with tag: ai generated, type: bug fix, inst: jax-rs, inst: spring but don't have permissions to do so

@katzj
katzj marked this pull request as ready for review August 20, 2026 19:45
@katzj
katzj requested review from a team as code owners August 20, 2026 19:45
@katzj
katzj requested review from bric3 and jordan-wong and removed request for a team August 20, 2026 19:45

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40720eabb7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

katzj added 2 commits August 21, 2026 09:59
…to a non-5xx response

JAX-RS and Spring MVC resource/controller methods commonly signal a
non-2xx response by throwing an exception (e.g. NotFoundException,
ResponseStatusException, or a custom exception annotated with
@ResponseStatus) that the framework's own exception-mapping machinery
turns into a normal HTTP response. The tracer previously flagged the
resource/controller span as an error unconditionally whenever such an
exception was thrown, even when the framework maps it to a routine
4xx (or other non-5xx) status - flooding error tracking with
non-actionable "errors" for expected control flow.

These exceptions already carry their intended status (WebApplicationException's
embedded Response, ResponseStatusException/ErrorResponse, or the
@ResponseStatus annotation), so the fix decides the error flag from
that status against the same "server error" set used for the root
HTTP span, instead of unconditionally erroring.

@ResponseStatus's value() and code() attributes are @aliasfor each
other, but plain reflection on the annotation proxy doesn't resolve
that aliasing - if a caller sets only code(), value() still reports
its own default (INTERNAL_SERVER_ERROR) rather than the value mirrored
from code(). Both Spring decorators now read code() first (reflectively
on the Spring 3.1 classpath, which predates code()'s introduction in
4.2) and fall back to value() only when code() is left at its default.
…TTP status

Some applications signal a response status via their own exception
hierarchy and a generic exception-handling advice, rather than any of
the JAX-RS/Spring conventions already handled (WebApplicationException,
ResponseStatusException, ErrorResponse, @ResponseStatus). Those
exceptions still get unconditionally flagged as errors even when they
map to a routine non-5xx response, since the tracer has no way to know
what status they carry.

DD_TRACE_RESPONSE_STATUS_EXCEPTIONS / trace.response-status.exceptions
lets users declare a list of fully.qualified.ExceptionClass#accessorMethod
entries; when a thrown exception (or a subclass of one) matches, the
named no-arg accessor is invoked reflectively and its numeric return
value is used the same way as the built-in status extraction. This
keeps the change fully opt-in and precise - it only ever reflects on
classes/methods a user explicitly named, rather than guessing at common
accessor names across arbitrary exceptions and risking a genuine error
being silently cleared.

A misconfigured accessor could return a value outside the valid HTTP
status range (e.g. -1 as an unknown-status sentinel). That value would
otherwise flow straight into Config#getHttpServerErrorStatuses, a
BitSet indexed by status code, which throws on a negative index,
aborting normal error handling for the span entirely. The accessor
return value is now validated as a plausible HTTP status (100-599)
before use, falling back to normal error handling otherwise.
@katzj
katzj force-pushed the katzj/fix-error-flag-on-mapped-exceptions branch from 40720ea to 93e9b3b Compare August 21, 2026 13: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.

1 participant