Skip to content

propagateExceptionFinalResort on web, add reportError - #4631

Merged
murfel merged 11 commits into
developfrom
propagateExceptionFinalResort-web-reportError
Apr 1, 2026
Merged

murfel merged 11 commits into
developfrom
propagateExceptionFinalResort-web-reportError

Conversation

@murfel

@murfel murfel commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

As requested by @turansky in #4451

@murfel
murfel requested a review from dkhalanskyjb March 3, 2026 16:49
@turansky

turansky commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

My common request - avoid js blocks - they are redundant :(

@murfel

murfel commented Mar 3, 2026

Copy link
Copy Markdown
Contributor Author

Yes, I remember our discussion, but I'd like to merge this as is for now, since it's been a long-standing issue. Later we could apply a refactoring to all our web codebase.

@dkhalanskyjb

Copy link
Copy Markdown
Collaborator

Please describe at least a manual testing procedure for this (it would be challenging to test automatically, since we don't have browser tests enabled, that much is clear). Figuring out from scratch any way to check the validity of this code every time we need to may take a lot of effort, given that Bun and Deno are not part of our daily lives.

error.stack = stack;
setTimeout(function () { throw error }, 0);
if (typeof globalThis.reportError === 'function') {
// Modern browsers and some non-browser JS runtimes (Deno, Bun)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unclear why we should choose it over setTimeout supported everywhere. If there are any reasons to use reportError if it is available, it's worth explicitly mentioning them here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benefit should be clear if we look at this example:

suspend fun main() {
    registerErrorHandler()
    supervisorScope {
        launch {
            val suppressed = IllegalStateException("suppressed")
            val exception = Exception("test")
            exception.addSuppressed(suppressed)
            throw exception
        }
    }
    println("Hello, Kotlin/JS!")
    var i = 0
    while (true) { // HANG FOREVER!!!
        ++i
    }
}

Trying to open this in a browser will lead to a hang. The browser will suggest stopping the script execution on the page. After you do, there's no error in sight, because we terminated the JS runtime before the setTimeout lambda managed to run.

On the other hand, reportError reports the error reliably.

For hanging pages, I'd want to learn of the critical errors that happened right before the hang, so reportError is a clear improvement.

@fzhinkin

fzhinkin commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

JFTR, there was a way to configure Wasm target to use Deno (https://github.com/Kotlin/kotlin-wasm-wasi-template/blob/ilgonmic/prototype-runtimes/buildSrc/src/main/kotlin/deno/declare.kt), perhaps, a similar trick could be applied to JS too.

@murfel

murfel commented Mar 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Manual testing for browser

I prepped a small demo. Open in IDEA and click run in the run configuration dropdown, a page in browser will open, right-click inspect (in Chrome), go to the console tab, and see the error printed to the console.

To test the proposed behaviour, checkout this branch, publish this branch locally with ./gradlew publishToMavenLocal, and uncomment mavenLocal() and the library dependency in build.gradle.kts.

https://github.com/Kotlin/kmp-js-coroutines

The console printing style changes significantly (see screenshots). Currently, you can clearly see that it came from progagateExceptionFinalResort once uncollapsed. With reportError you can see the suppressed exception (although buried in a stacktrace).

Another behaviour difference is that currently an error event listener wouldn't intercept the exception, but with reportError, it would. (Play around in the demo, registerErrorHandler.)

Current behaviour (uncollapsed error dropdown):
Screenshot 2026-03-09 at 16 28 41

Proposed behaviour with reportError:
Screenshot 2026-03-09 at 14 48 35

@murfel
murfel requested a review from fzhinkin March 24, 2026 12:44
@murfel

murfel commented Mar 24, 2026

Copy link
Copy Markdown
Contributor Author

@fzhinkin would you like to discuss or add something else? If not, I'll merge.

@murfel
murfel merged commit 6717207 into develop Apr 1, 2026
1 check passed
@murfel
murfel deleted the propagateExceptionFinalResort-web-reportError branch April 1, 2026 13:18
markharding pushed a commit to futo-org/Harbor that referenced this pull request Oct 13, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [org.jetbrains.kotlinx:kotlinx-coroutines-test](https://github.com/Kotlin/kotlinx.coroutines) | `1.10.1` β†’ `1.11.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlinx:kotlinx-coroutines-test/1.11.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlinx:kotlinx-coroutines-test/1.10.1/1.11.0?slim=true) |
| [org.jetbrains.kotlinx:kotlinx-coroutines-android](https://github.com/Kotlin/kotlinx.coroutines) | `1.10.1` β†’ `1.11.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlinx:kotlinx-coroutines-android/1.11.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlinx:kotlinx-coroutines-android/1.10.1/1.11.0?slim=true) |
| [org.jetbrains.kotlinx:kotlinx-coroutines-core](https://github.com/Kotlin/kotlinx.coroutines) | `1.10.1` β†’ `1.11.0` | ![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlinx:kotlinx-coroutines-core/1.11.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlinx:kotlinx-coroutines-core/1.10.1/1.11.0?slim=true) |

---

### Release Notes

<details>
<summary>Kotlin/kotlinx.coroutines (org.jetbrains.kotlinx:kotlinx-coroutines-test)</summary>

### [`v1.11.0`](https://github.com/Kotlin/kotlinx.coroutines/blob/HEAD/CHANGES.md#Version-1110)

[Compare Source](Kotlin/kotlinx.coroutines@1.10.2...1.11.0)

##### Various

- Kotlin was updated to 2.2.20 ([#&#8203;4545](Kotlin/kotlinx.coroutines#4545)).
- Improved the published jar files ([#&#8203;3842](Kotlin/kotlinx.coroutines#3842), [#&#8203;4599](Kotlin/kotlinx.coroutines#4599)).
- Various documentation improvements, including complete rewrites of structured concurrency and error handling-related KDoc ([#&#8203;4433](Kotlin/kotlinx.coroutines#4433), [#&#8203;4596](Kotlin/kotlinx.coroutines#4596)).

##### Breaking changes and deprecations

- Moved `Promise`-related functions from JS and Wasm/JS to the new `web` target. On Wasm/JS, this is a breaking change. Before the change, `Promise` on Wasm/JS could work with arbitrary Kotlin types, but now, only `JsAny` subtypes are accepted ([#&#8203;4563](Kotlin/kotlinx.coroutines#4563)).
- Changed handling of coroutine exceptions that can't be propagated on JS and Wasm/JS. Before, exceptions were logged, but now, they are reported to the JS runtime ([#&#8203;4451](Kotlin/kotlinx.coroutines#4451), [#&#8203;4631](Kotlin/kotlinx.coroutines#4631)).
- Deprecated using `CoroutineDispatcher` as the coroutine context key; now, `ContinuationInterceptor` has to be used instead ([#&#8203;4333](Kotlin/kotlinx.coroutines#4333)).
- Advanced the deprecation levels on `kotlinx-coroutines-test` APIs ([#&#8203;4604](Kotlin/kotlinx.coroutines#4604)).
- Added lint functions that mark passing a `Job` to coroutine builders as deprecated ([#&#8203;4435](Kotlin/kotlinx.coroutines#4435)).

##### Bug fixes and improvements

- Added a `callsInPlace(EXACTLY_ONCE)` contract to `runBlocking` in code shared between JVM and Native ([#&#8203;4368](Kotlin/kotlinx.coroutines#4368)).
- Added a `callsInPlace(EXACTLY_ONCE)` contract to `suspendCancellableCoroutine` ([#&#8203;4574](Kotlin/kotlinx.coroutines#4574)).
- Fixed `flowOn` incorrectly handling `ThreadContextElement` updates ([#&#8203;4403](Kotlin/kotlinx.coroutines#4403)).
- Fixed exceptions in user-supplied `Thread.UncaughtExceptionHandler` instances causing the internal coroutines machinery to fail ([#&#8203;4516](Kotlin/kotlinx.coroutines#4516)).
- Fixed `CoroutineDispatcher.asScheduler` in the RxJava integration not cancelling outstanding work when a `Worker` gets cancelled, which led to memory leaks in some scenarios ([#&#8203;4615](Kotlin/kotlinx.coroutines#4615)).
- Fixed `SharedFlow` entering an invalid state when a subscriber and an emitter are cancelled simultaneously ([#&#8203;4583](Kotlin/kotlinx.coroutines#4583)).
- Fixed an R8 optimization leading to `shareIn`/`stateIn` coroutines getting garbage-collected ([#&#8203;4646](Kotlin/kotlinx.coroutines#4646)). Thanks, [@&#8203;solevic](https://github.com/solevic)!

##### Small additions

- Added `CompletableDeferred.asDeferred` for obtaining a read-only `Deferred` view ([#&#8203;4408](Kotlin/kotlinx.coroutines#4408)).
- Added `SharedFlow.asFlow` for obtaining a `Flow` view with hidden hot flow semantics ([#&#8203;4530](Kotlin/kotlinx.coroutines#4530)). Thanks, [@&#8203;g000sha256](https://github.com/g000sha256)!
- Added a `StateFlow.collectLatest` overload returning `Nothing` to assist with finding unreachable code ([#&#8203;4454](Kotlin/kotlinx.coroutines#4454)).
- Added `ReceiveChannel.consumeTo` for consuming a `ReceiveChannel` into a `MutableCollection` ([#&#8203;4520](Kotlin/kotlinx.coroutines#4520)).
- Added a `StateFlow<T>.onSubscription` overload returning a `StateFlow<T>`, similar to `SharedFlow<T>.onSubscription` returning `SharedFlow<T>` ([#&#8203;4275](Kotlin/kotlinx.coroutines#4275)). Thanks, [@&#8203;xit0c](https://github.com/xit0c)!
- Added terminal `Flow` operators for collecting a `Flow` to a `Map` ([#&#8203;1541](Kotlin/kotlinx.coroutines#1541)).

##### Changelog relative to version 1.11.0

No changes, only the version is increased.

### [`v1.10.2`](https://github.com/Kotlin/kotlinx.coroutines/blob/HEAD/CHANGES.md#Version-1102)

[Compare Source](Kotlin/kotlinx.coroutines@1.10.1...1.10.2)

- Fixed the `kotlinx-coroutines-debug` JAR file including the `module-info.class` file twice, resulting in failures in various tooling ([#&#8203;4314](Kotlin/kotlinx.coroutines#4314)). Thanks, [@&#8203;RyuNen344](https://github.com/RyuNen344)!
- Fixed `Flow.stateIn` hanging when the scope is cancelled in advance or the flow is empty ([#&#8203;4322](Kotlin/kotlinx.coroutines#4322)). Thanks, [@&#8203;francescotescari](https://github.com/francescotescari)!
- Improved handling of dispatcher failures in `.limitedParallelism` ([#&#8203;4330](Kotlin/kotlinx.coroutines#4330)) and during flow collection ([#&#8203;4272](Kotlin/kotlinx.coroutines#4272)).
- Fixed `runBlocking` failing to run its coroutine to completion in some cases if its JVM thread got interrupted ([#&#8203;4399](Kotlin/kotlinx.coroutines#4399)).
- Small tweaks, fixes, and documentation improvements.

</details>

---

### Configuration

πŸ“… **Schedule**: (UTC)

- Branch creation
  - "before 9am on tuesday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

β™» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

πŸ”• **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMzMuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjEzMy4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcCIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiLCJyZW5vdmF0ZSJdfQ==-->

Reviewed-on: https://code.futo.org/harbor/harbor/pulls/1829
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.

4 participants