-
Notifications
You must be signed in to change notification settings - Fork 8
Exception stack trace truncated — cause chain not fully reported #118
Description
Summary
Generated by Claude
When an exception propagates out of a cell, JJava truncates the cause chain. Only the top-level wrapper exception and one level of Caused by (a jdk.jshell.EvalException) are shown. Any deeper causes — including the true root cause — are silently dropped, making it very hard to diagnose errors.
Steps to Reproduce
Run any code that throws an exception with a multi-level cause chain. A typical scenario is calling a method that catches a low-level exception (e.g. a NullPointerException inside a third-party library), wraps it in a domain exception, and throws that — producing a chain of two or more Caused by levels.
Actual Behavior
JJava prints only two levels of the exception chain:
java.lang.RuntimeException: com.example.MyServiceException, Operation failed
| at org.dflib.jjava.kernel.execution.CodeEvaluator.evalSingle(CodeEvaluator.java:145)
| at org.dflib.jjava.kernel.execution.CodeEvaluator.eval(CodeEvaluator.java:79)
| at org.dflib.jjava.kernel.JavaKernel.doEval(JavaKernel.java:230)
| ...
| Caused by: jdk.jshell.EvalException: Operation failed
| at com.example.client.MyClient.performAction(MyClient.java:116)
| at com.example.client.MyClient.execute(MyClient.java:46)
| at .(<Anonymous>#61:1)
The exception chain ends here. The two deeper Caused by frames — the intermediate library error and the root-cause NullPointerException — are missing entirely.
Expected Behavior
The full exception chain should be reported, matching what the JVM itself produces:
com.example.MyServiceException: Operation failed
at com.example.client.MyClient.performAction(MyClient.java:116)
at com.example.client.MyClient.execute(MyClient.java:46)
...
Caused by: Cannot invoke "String.length()" because "this.input" is null
at com.thirdparty.lib.SomeClient.doRequest(SomeClient.java:161)
at com.example.client.MyClient.performAction(MyClient.java:114)
... 9 more
Caused by: java.lang.NullPointerException: Cannot invoke "String.length()" because "this.input" is null
at java.base/java.net.URI$Parser.parse(URI.java:3194)
at java.base/java.net.URI.<init>(URI.java:649)
...
Root Cause (hypothesis)
CodeEvaluator catches the jdk.jshell.EvalException thrown by JShell and re-wraps it in a plain RuntimeException. In doing so, it likely reconstructs only one level of cause rather than recursively walking EvalException.getCause() to rebuild the full chain.
The relevant location appears to be CodeEvaluator.evalSingle() (line 145).
Impact
Without the root cause, debugging exceptions that originate inside third-party libraries is practically impossible from inside the notebook. The user is forced to reproduce the error outside of Jupyter to get a useful stack trace.