Fix ConfigValues.observe to catch provider exceptions#196
Conversation
ConfigValues KDoc promises that observe wraps provider calls in try/catch
and routes errors through onProviderError, but the implementation collected
provider Flows raw, so a provider whose Flow throws downstream would crash
the host coroutine. Wrap local and remote Flows in .catch { onProviderError }
before merging to restore the documented contract. Add a regression test
asserting that a throwing local provider does not propagate and that the
error is reported.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
There was a problem hiding this comment.
Pull request overview
Fixes a bug in ConfigValues.observe where exceptions thrown by localProvider.observe(param) propagated to consumers and crashed the host coroutine, contradicting the documented contract that the flow does not terminate on provider error. The fix wraps both upstream flows in .catch so provider errors are routed to the onProviderError callback, matching the existing behavior of getValue.
Changes:
- Apply
.catch { e -> onProviderError(e) }to bothlocalFlowandremoteFlowinobservebefore merging. - Add a regression test
ProviderErrorObserveTestusing Turbine to verify that a throwing local provider does not propagate the error and that emissions before the error are still collected.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| core/src/commonMain/kotlin/dev/androidbroadcast/featured/ConfigValues.kt | Wraps the local and remote upstream flows in .catch to forward provider exceptions to onProviderError instead of crashing the consumer. |
| core/src/commonTest/kotlin/dev/androidbroadcast/featured/ProviderErrorObserveTest.kt | New test verifying observe survives a throwing local provider flow and routes the error to onProviderError. |
Summary
ConfigValues.observecollected provider Flows raw viamergewith no.catch, so any exception thrown downstream bylocalProvider.observe(param)propagated straight into the consumer's coroutine and crashed the host — violating the documented contract ("flow does not terminate on provider error").localFlowandremoteFlowin.catch { e -> onProviderError(e) }before passing them tomerge. The Flow operator.catchignoresCancellationExceptionby design, so no manual rethrow is needed.getValuewas already guarded viarunCatching; this patch makesobserveconsistent with it.Regression test
ProviderErrorObserveTest.observeDoesNotPropagateWhenLocalProviderFlowThrows(
core/src/commonTest/kotlin/dev/androidbroadcast/featured/ProviderErrorObserveTest.kt)Constructs a fake
LocalConfigValueProviderwhoseobserve()emits one value then throwsIllegalStateException("simulated provider error"), collects via Turbine, and asserts:onProviderErrorreceives exactly oneIllegalStateExceptionwith the expected messageFollow-up PRs enabled
This fix unblocks PRs that register providers with types they don't support (e.g. DataStore enum converters) — the flow will now degrade gracefully rather than crashing.
Test plan
./gradlew :core:allTests— green (JVM + iOS simulator)./gradlew spotlessCheck— green./gradlew :core:koverVerify— ≥90% coverage gate passes