Skip to content

fix(jextract/jni): preserve underlying exception in async closures - #959

Open
codingnoobno1 wants to merge 2 commits into
swiftlang:mainfrom
codingnoobno1:fix/jni-preserve-async-exception-cause
Open

codingnoobno1 wants to merge 2 commits into
swiftlang:mainfrom
codingnoobno1:fix/jni-preserve-async-exception-cause

Conversation

@codingnoobno1

@codingnoobno1 codingnoobno1 commented Nov 9, 2026 •

Copy link
Copy Markdown

Problem

When a Java async closure completes exceptionally, Future.get() throws java.util.concurrent.ExecutionException. The async JNI thunk passed that wrapper to completeExceptionally, so the outer future lost the original throwable.

Changes

Unwrap one ExecutionException in JNIEnvironment.completeFutureExceptionally and pass its cause to completeExceptionally. Other errors still go through makeJavaThrowable. The generated async catch calls this helper, and a jextract snapshot test covers that call.

Fixes #915

Test plan

  • swift test --disable-xctest --disable-experimental-prebuilts --filter asyncFailurePreservesJavaExceptionCause on Swift 6.3 (noble) and JDK 25
  • Upstream CI on this pull request

Future.get() reports a failed Java future as ExecutionException. Unwrap that one layer before completeExceptionally so the outer future keeps the original throwable.

Fixes swiftlang#915

Co-authored-by: Cursor <cursoragent@cursor.com>
@codingnoobno1
codingnoobno1 requested a review from ktoso as a code owner November 9, 2026 07:12
The generator check never calls future.get(). Throw ExecutionException with a RuntimeException cause and assert the outer future still reports that cause.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

jextract: Preserve underlying exception when async closure completes exceptionally

1 participant