Fix NullPointerException in ThrowableStackTraceRenderer for non-idempotent getCause() - #4303
Open
UsmanEjaz10 wants to merge 6 commits into
Open
Fix NullPointerException in ThrowableStackTraceRenderer for non-idempotent getCause()#4303UsmanEjaz10 wants to merge 6 commits into
NullPointerException in ThrowableStackTraceRenderer for non-idempotent getCause()#4303UsmanEjaz10 wants to merge 6 commits into
Conversation
…dempotent `getCause()` (apache#4279) Rendering walked the causal chain twice. The render pass called `Throwable#getCause()` a second time after the metadata pre-computation pass had keyed the first result in its `IdentityHashMap`. A non-idempotent `getCause()` returns a different instance on that second call, the lookup misses, and rendering dereferences a null `Metadata` (`stackLength` NPE). Capture the cause once in `Context.Metadata` during the metadata pass and reuse `metadata.cause` at render time, in both the base and inverted renderers - the same treatment already applied to `getStackTrace()` (apache#3940) and `getSuppressed()` (apache#3929). The extended renderer (`%xEx`) keeps its own JAR-enrichment walk, which null-checks and is a legitimate second `getCause()` caller. Adds `NonIdempotentGetCauseTest` covering `%ex`, `%rEx` (invoked once) and `%xEx` (invoked twice). Signed-off-by: UsmanEjaz10 <[email protected]> Co-Authored-By: Claude Code <[email protected]>
Contributor
|
@UsmanEjaz10 Noted!. Give some time i will review it. Thanks for the contribution! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Rendering a
Throwablewalks its causal chain in two passes: a metadata pre-computation pass and a render pass. The render pass calledThrowable#getCause()a second time after the metadata pass had keyed the first result in itsIdentityHashMap. For agetCause()override that returns a different instance on every call (legal per theThrowableAPI contract), the second lookup misses, and rendering dereferences a nullMetadata, throwing:Fix
Capture the cause once in
Context.Metadataduring the metadata pass and reusemetadata.causeat render time — in the base renderer (%ex, and%xExwhich inherits it) and in the inverted renderer (%rEx). This is the same treatment the class already applies togetStackTrace()(#3940) andgetSuppressed()(#3929).ThrowableExtendedStackTraceRendereris intentionally left alone: its independent JAR-enrichment walk null-checks and degrades gracefully, and it is a legitimate secondgetCause()caller — so%xExinvokesgetCause()twice by design.Tests
New
NonIdempotentGetCauseTestasserts the fix across all three converters:%ex/%rEx:getCause()invoked exactly once per render.%xEx: invoked exactly twice (its own enrichment walk is the second, legitimate call).getCause()renders its cause without throwing.Kept as a standalone class rather than added to the shared
AbstractStackTraceTest, which mutates a staticEXCEPTIONconcurrently and makes adding test methods there unsafe w.r.t. execution order.Verification
./mvnw verify(scoped tolog4j-core,log4j-core-testand upstream modules): passes — RAT, Spotless, japicmp all green.src/changelog/.2.x.x/.Fixes #4279