From 35463367fe73dbea7cfc1de343321e795db4b38f Mon Sep 17 00:00:00 2001 From: Mikhail Dzianishchyts Date: Tue, 14 Apr 2026 15:03:18 +0300 Subject: [PATCH] Use wrapping exceptions to fix truncated stack traces #118 --- .../org/dflib/jjava/kernel/JavaKernel.java | 26 +++++++++++++++---- .../execution/JJavaExecutionControl.java | 16 +++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/jjava-kernel/src/main/java/org/dflib/jjava/kernel/JavaKernel.java b/jjava-kernel/src/main/java/org/dflib/jjava/kernel/JavaKernel.java index 2d64005..ac5e2b9 100644 --- a/jjava-kernel/src/main/java/org/dflib/jjava/kernel/JavaKernel.java +++ b/jjava-kernel/src/main/java/org/dflib/jjava/kernel/JavaKernel.java @@ -127,6 +127,8 @@ protected List formatError(Throwable e) { return formatEvaluationTimeoutException((EvaluationTimeoutException) e); } else if (e instanceof EvaluationInterruptedException) { return formatEvaluationInterruptedException((EvaluationInterruptedException) e); + } else if (e instanceof RuntimeException && e.getCause() instanceof EvalException) { + return formatEvalException((EvalException) e.getCause()); } else { return new ArrayList<>(super.formatError(e)); } @@ -180,16 +182,30 @@ private List formatIncompleteSourceException(IncompleteSourceException e private List formatEvalException(EvalException e) { List fmt = new ArrayList<>(); - - String evalExceptionClassName = EvalException.class.getName(); String actualExceptionName = e.getExceptionClassName(); - super.formatError(e).stream() - .map(line -> line.replace(evalExceptionClassName, actualExceptionName)) - .forEach(fmt::add); + fmt.add(errorStyler.secondary(actualExceptionName + ": " + e.getMessage())); + for (StackTraceElement element : e.getStackTrace()) { + fmt.add(errorStyler.secondary("\tat " + element)); + } + formatEvalExceptionCause((EvalException) e.getCause(), fmt); return fmt; } + private void formatEvalExceptionCause(EvalException e, List fmt) { + if (e == null) { + return; + } + + String actualExceptionName = e.getExceptionClassName(); + fmt.add(errorStyler.secondary("Caused by: " + actualExceptionName + ": " + e.getMessage())); + for (StackTraceElement element : e.getStackTrace()) { + fmt.add(errorStyler.secondary("\tat " + element)); + } + + formatEvalExceptionCause((EvalException) e.getCause(), fmt); + } + private List formatUnresolvedReferenceException(UnresolvedReferenceException e) { List fmt = new ArrayList<>(); diff --git a/jjava-kernel/src/main/java/org/dflib/jjava/kernel/execution/JJavaExecutionControl.java b/jjava-kernel/src/main/java/org/dflib/jjava/kernel/execution/JJavaExecutionControl.java index e17e79a..f85b735 100644 --- a/jjava-kernel/src/main/java/org/dflib/jjava/kernel/execution/JJavaExecutionControl.java +++ b/jjava-kernel/src/main/java/org/dflib/jjava/kernel/execution/JJavaExecutionControl.java @@ -131,6 +131,18 @@ protected String invoke(Method doitMethod) throws Exception { return id; } + private RunException wrapInRunException(Throwable cause) { + if (cause instanceof SPIResolutionException) { + return new ResolutionException(((SPIResolutionException) cause).id(), cause.getStackTrace()); + } + + UserException ue = new UserException(String.valueOf(cause.getMessage()), cause.getClass().getName(), cause.getStackTrace()); + if (cause.getCause() != null) { + ue.initCause(wrapInRunException(cause.getCause())); + } + return ue; + } + private Object doInvoke(String id, Method doitMethod) throws Exception { Future task = isNestedCall() @@ -160,10 +172,8 @@ private Object doInvoke(String id, Method doitMethod) throws Exception { } if (cause == null) { throw new UserException("null", "Unknown Invocation Exception", e.getStackTrace()); - } else if (cause instanceof SPIResolutionException) { - throw new ResolutionException(((SPIResolutionException) cause).id(), cause.getStackTrace()); } else { - throw new UserException(String.valueOf(cause.getMessage()), cause.getClass().getName(), cause.getStackTrace()); + throw wrapInRunException(cause); } } catch (TimeoutException e) { String message = String.format("Execution timed out after configured timeout of %d %s.",