Skip to content

[EXTERNAL] fix(google): guard showInAppMessages against BillingClient runtime crashes#3367

Merged
MonikaMateska merged 1 commit into
RevenueCat:external/matteinn/fix/#3367-billingwrapper-npefrom
matteinn:fix/#3273-BillingWrapper$showInAppMessagesIfNeeded-npe
Apr 20, 2026
Merged

[EXTERNAL] fix(google): guard showInAppMessages against BillingClient runtime crashes#3367
MonikaMateska merged 1 commit into
RevenueCat:external/matteinn/fix/#3367-billingwrapper-npefrom
matteinn:fix/#3273-BillingWrapper$showInAppMessagesIfNeeded-npe

Conversation

@matteinn

@matteinn matteinn commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids

Motivation

This PR hardens showInAppMessagesIfNeeded against a remaining Play Billing crash path.
Resolves #3273, which has not been fully solved by a previous tentative.

Description

Even after lifecycle/window attachment checks, BillingClient.showInAppMessages(...) can still throw a RuntimeException (including NullPointerException) due to an internal race in Play Billing.

This is a defensive boundary around third-party BillingClient behavior: it complements previous activity/window checks by handling exceptions that can still occur between pre-checks and BillingClient’s internal view/token access.

Here's exactly what changed:

  • Wrapped the showInAppMessages(...) invocation in BillingWrapper with a try/catch
  • On exception, log the error and skip showing the in-app message instead of crashing the app
  • Added new log string: BILLING_INAPP_MESSAGE_SHOW_EXCEPTION
  • Added a regression unit test that simulates showInAppMessages(...) throwing a NullPointerException and verifies that there are no crash and that the error is logged

Note

Low Risk
Low risk defensive change that only adds exception handling and logging around a third-party call; behavior changes only when Play Billing throws unexpectedly.

Overview
Prevents a remaining Play Billing crash path by wrapping BillingClient.showInAppMessages(...) in BillingWrapper.showInAppMessagesIfNeeded with a try/catch and logging an error instead of crashing.

Adds a new log string (BILLING_INAPP_MESSAGE_SHOW_EXCEPTION) and a regression unit test that simulates showInAppMessages throwing (e.g., NullPointerException) and asserts it is logged and does not crash.

Reviewed by Cursor Bugbot for commit 5e8afd5. Bugbot is set up for automated code reviews on this repo. Configure here.

@matteinn matteinn requested a review from a team as a code owner April 20, 2026 08:10
@MonikaMateska

Copy link
Copy Markdown
Member

Hi @matteinn, thanks for the fix! We're taking a look.

@MonikaMateska MonikaMateska changed the base branch from main to external/matteinn/fix/#3367-billingwrapper-npe April 20, 2026 08:49
@MonikaMateska MonikaMateska changed the title fix(google): guard showInAppMessages against BillingClient runtime crashes [EXTERNAL] fix(google): guard showInAppMessages against BillingClient runtime crashes Apr 20, 2026
@MonikaMateska MonikaMateska merged commit 6aaaa4e into RevenueCat:external/matteinn/fix/#3367-billingwrapper-npe Apr 20, 2026
3 checks passed
github-merge-queue Bot pushed a commit that referenced this pull request Apr 20, 2026
… runtime crashes (#3367) by @matteinn (#3368)

<!-- Thank you for contributing to Purchases! Before pressing the
"Create Pull Request" button, please provide the following: -->

### Checklist
- [x] If applicable, unit tests
- [ ] If applicable, create follow-up issues for `purchases-ios` and
hybrids

### Motivation
<!-- Why is this change required? What problem does it solve? -->
<!-- Please link to issues following this format: Resolves #999999 -->
This PR hardens `showInAppMessagesIfNeeded` against a remaining Play
Billing crash path.
Resolves #3273,
which has not been fully solved by a previous
[tentative](#3274).

### Description
<!-- Describe your changes in detail -->
<!-- Please describe in detail how you tested your changes -->

Even after lifecycle/window attachment checks,
`BillingClient.showInAppMessages(...)` can still throw a
`RuntimeException` (including `NullPointerException`) due to an internal
race in Play Billing.

This is a defensive boundary around third-party BillingClient behavior:
it complements previous activity/window checks by handling exceptions
that can still occur between pre-checks and BillingClient’s internal
view/token access.

Here's exactly what changed:
- Wrapped the `showInAppMessages(...)` invocation in `BillingWrapper`
with a try/catch
- On exception, log the error and skip showing the in-app message
instead of crashing the app
- Added new log string: BILLING_INAPP_MESSAGE_SHOW_EXCEPTION
- Added a regression unit test that simulates `showInAppMessages(...)`
throwing a `NullPointerException` and verifies that there are no crash
and that the error is logged


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small defensive change limited to in-app message display: it only adds
exception handling and logging, plus a unit test, with minimal behavior
impact beyond avoiding a crash.
> 
> **Overview**
> Prevents app crashes when Google Play Billing’s
`BillingClient.showInAppMessages` throws a `RuntimeException` despite
lifecycle/window checks, by wrapping the call in a `try/catch` and
logging a new `BILLING_INAPP_MESSAGE_SHOW_EXCEPTION` error instead of
propagating the crash.
> 
> Adds a regression test that forces `showInAppMessages` to throw (e.g.,
`NullPointerException`) and asserts the wrapper logs the error and
continues without crashing.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
fc5cd08. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->



<!-- Thank you for contributing to Purchases! Before pressing the
"Create Pull Request" button, please provide the following: -->

### Checklist
- [ ] If applicable, unit tests
- [ ] If applicable, create follow-up issues for `purchases-ios` and
hybrids

### Motivation
<!-- Why is this change required? What problem does it solve? -->
<!-- Please link to issues following this format: Resolves #999999 -->

### Description
<!-- Describe your changes in detail -->
<!-- Please describe in detail how you tested your changes -->

---------

Co-authored-by: Matteo Innocenti <matteo@appstyx.com>
matteinn pushed a commit to matteinn/purchases-android that referenced this pull request Apr 28, 2026
**This is an automatic release.**

## RevenueCat SDK
### 🐞 Bugfixes
* fix: move Google BillingClient connection off the main thread (RevenueCat#3369)
via Toni Rico (@tonidero)
* [EXTERNAL] fix(google): guard showInAppMessages against BillingClient
runtime crashes (RevenueCat#3367) by @matteinn (RevenueCat#3368) via Monika Mateska
(@MonikaMateska)

## RevenueCatUI SDK
### Paywallv2
#### 🐞 Bugfixes
* Add Workflows network layer (RevenueCat#3300) via Cesar de la Vega (@vegaro)

### 🔄 Other Changes
* Fix `revenuecat.useWorkflowsEndpoint` compiler flag (RevenueCat#3374) via Cesar
de la Vega (@vegaro)
* Create paywall from workflow response. Add `USE_WORKFLOWS_ENDPOINT`
BuildConfig (RevenueCat#3350) via Cesar de la Vega (@vegaro)
* Refactor: Remove unnecessary lint suppressions (RevenueCat#3373) via cursor[bot]
(@cursor[bot])
* Bump fastlane-plugin-revenuecat_internal from `a1eed48` to `b822f01`
(RevenueCat#3371) via dependabot[bot] (@dependabot[bot])
* Bump fastlane from 2.232.2 to 2.233.0 (RevenueCat#3370) via dependabot[bot]
(@dependabot[bot])
* Attempt to fix `AssertionError` "ms is denormalized" in
`QueryPurchasesUseCaseTest` (RevenueCat#3361) via Cesar de la Vega (@vegaro)
* Update baseline profiles (RevenueCat#3296) via Jaewoong Eum (@skydoves)
* fix: reduce precision for flaky HeaderDirectHeroImage snapshot (RevenueCat#3362)
via Cesar de la Vega (@vegaro)
* Fix test failures reported twice (RevenueCat#3360) via Cesar de la Vega
(@vegaro)
* refactor: extract `updateStateFromOffering` in `PaywallViewModel`
(RevenueCat#3359) via Cesar de la Vega (@vegaro)
* [Fix] Include parent tabs component_name in tab-control switch
interaction events (RevenueCat#3358) via Monika Mateska (@MonikaMateska)
* Refactor: Remove unnecessary lint suppressions (RevenueCat#3348) via cursor[bot]
(@cursor[bot])
* fix: always upload CI test results even when tests fail (RevenueCat#3357) via
Cesar de la Vega (@vegaro)
* refactor: extract `RevenueCatDialogScaffold` (RevenueCat#3355) via Cesar de la
Vega (@vegaro)
* Fix Slack notifications for nightly integration tests (RevenueCat#3354) via Toni
Rico (@tonidero)
* UI events for paywall component interactions (RevenueCat#3287) via Monika
Mateska (@MonikaMateska)
* Bump fastlane-plugin-revenuecat_internal from `20911d1` to `a1eed48`
(RevenueCat#3351) via dependabot[bot] (@dependabot[bot])

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Primarily a version bump and release automation updates (docs
deploy/redirect and changelog); no functional library code changes
beyond updating embedded version constants.
> 
> **Overview**
> Cuts the `10.2.1` release by updating version references across the
repo (Gradle `VERSION_NAME`, internal `frameworkVersion`, sample/test
app dependency pins, and `.version`).
> 
> Updates the docs release pipeline and website redirect to publish and
point at `10.2.1`, and refreshes `CHANGELOG.md`/`CHANGELOG.latest.md`
with the 10.2.1 release notes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a0a325b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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.

NPE BillingWrapper$showInAppMessagesIfNeeded$2$2.invoke

2 participants