Skip to content

Use wrapping exceptions to fix truncated stack traces#121

Merged
andrus merged 1 commit into
dflib:mainfrom
m-dzianishchyts:118-fix-truncated-stack-traces
May 17, 2026
Merged

Use wrapping exceptions to fix truncated stack traces#121
andrus merged 1 commit into
dflib:mainfrom
m-dzianishchyts:118-fix-truncated-stack-traces

Conversation

@m-dzianishchyts
Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #118.

Details

The root cause is that JShell's UserException class doesn't preserve the Throwable cause field during serialization, it only carries message, causeExceptionClass, and stackTrace elements. When the exception chain is converted to EvalException by JShell, intermediate causes are discarded.

We can recursively wrap the entire exception cause chain in UserException's using initCause() before passing to JShell. This allows JShell to properly reconstruct the full exception chain.

Testing

Tested manually in jupyter notebook with a test snippet. Full exception chain is now displayed.

@andrus andrus merged commit 17d6ae8 into dflib:main May 17, 2026
5 checks passed
@andrus
Copy link
Copy Markdown
Contributor

andrus commented May 17, 2026

I am accepting the fix, as now I can see the full stack trace (still on the fence whether we should keep showing parent kernel stack around a user exception 🤔)

But I can barely understand what's going on in our exception handling inside both JJavaExecutionControl and JavaKernel.formatError(..) 🙂 JShell convoluted exception topology is partially to blame, but we add more of that on top. E.g., this is very confusing (and when you look inside wrapInRunException(..) it becomes even more so) :

catch (ExecutionException e) {

    Throwable cause = e.getCause();
    if (cause instanceof InvocationTargetException) {
        cause = cause.getCause();
    }
    if (cause == null) {
        throw new UserException("null", "Unknown Invocation Exception", e.getStackTrace());
    } else {
        throw wrapInRunException(cause);
    }
}

Maybe we can identify a finite set of specific exceptions cases and refactor them all into named methods or something? Or maybe there's a common way to unwind exceptions to a known cause that can be handled in a unified way?

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.

Exception stack trace truncated — cause chain not fully reported

2 participants