Skip to content

Fix default error handling for subscribe(with:) - #2731

Open
heoblitz wants to merge 5 commits into
ReactiveX:mainfrom
heoblitz:fix/default-error-handler-with-subscription
Open

heoblitz wants to merge 5 commits into
ReactiveX:mainfrom
heoblitz:fix/default-error-handler-with-subscription

Conversation

@heoblitz

@heoblitz heoblitz commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Closes #2730

Summary

subscribe(with:) always passed a weak error-forwarding closure to its underlying subscription, even when the caller omitted onError or onFailure. That closure prevented unhandled terminal errors from reaching Hooks.defaultErrorHandler.

This change forwards an error closure only when the caller provides one. Without an explicit callback, the existing subscription implementation receives nil and invokes Hooks.defaultErrorHandler.

The behavior is aligned across Observable, Single, Maybe, and Completable.

Root cause and impact

The object-based overload previously converted an optional error callback into a non-optional closure. When the callback was absent, the closure simply returned, but its presence still made the underlying subscription consider the error handled.

Explicit onError and onFailure callbacks retain their existing behavior, including their weak object capture. The only observable change is that an error without an explicit callback is now handled through the configured default hook, as with the corresponding non-object subscription overload.

The test that customizes global hook state now restores that state after it finishes.

Validation

  • Added regression coverage for all four affected types.
  • Verified that explicit error callbacks receive the original error and do not invoke the default hook.
  • Verified that omitted error callbacks invoke Hooks.defaultErrorHandler.
  • Ran ObservableSubscriptionTest, SingleTest, MaybeTest, and CompletableTest with the macOS AllTests-macOS scheme.
  • Ran git diff --check.

@heoblitz
heoblitz force-pushed the fix/default-error-handler-with-subscription branch 5 times, most recently from 0e8a3c1 to a804683 Compare September 3, 2026 14:28
Comment thread Tests/RxSwiftTests/PrimitiveSequence+ConcurrencyTests.swift Outdated
@freak4pc

freak4pc commented Sep 4, 2026

Copy link
Copy Markdown
Member

Thanks for this — the core fix is right, and mapping the optional through so the underlying subscribe sees nil is exactly the correct approach. Coverage looks complete too: those four are the only onError?(object, / onFailure?(object, sites, and Infallible has no error path. Restoring the Hooks state in defer is a good catch as well.

One request before I merge: could you drop a8046832 ("Improve test stability") and rebase on main?

It rewrites the three race tests in PrimitiveSequence+ConcurrencyTests.swift, which is unrelated to the subscribe(with:) fix, and it collides with #2727 — that PR reworks the same three tests as part of fixing the underlying cancellation deadlock. #2727 is going in first.

Its version is also the stronger one: it sets expectedFulfillmentCount = iterations so a task that never finishes actually fails the test. The version here keeps if i == iterations - 1 { expectation.fulfill() }, which under concurrentPerform makes the subsequent wait trivially satisfied (the loop already blocks until every iteration returns), and it drops the iteration count from 10 000 to 1 000.

Everything else in the PR is good to go.

@heoblitz
heoblitz force-pushed the fix/default-error-handler-with-subscription branch from a804683 to 452b800 Compare September 5, 2026 02:35
@heoblitz
heoblitz force-pushed the fix/default-error-handler-with-subscription branch from 452b800 to 1db231a Compare September 5, 2026 02:44
@heoblitz

heoblitz commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

@freak4pc Got it — looks like this was already being handled in the other PR. As requested, I dropped the test commit and rebased on main. Thanks for the review!

@heoblitz

heoblitz commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

The tests had been failing because of this, but the latest commit with the test changes passed:
https://github.com/ReactiveX/RxSwift/actions/runs/33767087039

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.

Hooks.defaultErrorHandler is bypassed by subscribe(with:) without an error callback

2 participants