Skip to content

[pigeon] Treat NSNull as null for non-null Flutter API returns - #12531

Merged
auto-submit[bot] merged 5 commits into
flutter:mainfrom
Xelorium:pigeon-swift-nsnull-null-check
Sep 15, 2026
Merged

auto-submit[bot] merged 5 commits into
flutter:mainfrom
Xelorium:pigeon-swift-nsnull-null-check

Conversation

@Xelorium

@Xelorium Xelorium commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Description

FlutterStandardReader substitutes NSNull for a nil element when it decodes a list, so a null reply for a non-null return value reaches the generated Swift code as NSNull rather than as nil. The generated guard only checks listResponse[0] == nil, so NSNull falls through to the force cast below it and aborts the process:

} else if listResponse[0] == nil {
  completion(.failure(PigeonError(code: "null-error", ...)))
} else {
  let result = listResponse[0] as! AuthenticationChallengeResponse  // 💥

This crashes production apps through webview_flutter_wkwebview: the plugin clears its native instance manager in WebViewFlutterPlugin.tearDownProxyAPIRegistrar(), which iOS triggers on sceneDidDisconnect / applicationWillTerminate. A WKNavigationDelegate.didReceiveAuthenticationChallenge reply that is still in flight then carries an identifier the native instance manager can no longer resolve, the codec reader returns nil, and the app dies with:

Could not cast value of type 'NSNull' (0x...) to 'webview_flutter_wkwebview.AuthenticationChallengeResponse' (0x...).

The same shape was reported before for other types (URLRequestWrapper in flutter/flutter#162437), which is expected: the generator emits this guard for every non-null return value, so any unresolvable instance crashes instead of reporting an error.

This PR treats NSNull as a null reply, so the existing null-error path handles it.

Verification

  • Added a Swift generator unit test asserting the generated guard covers NSNull.
  • Added a native regression test in platform_tests that replies NSNull to a non-null Flutter API return. It passes with this change; with the generated guard reverted it aborts with Could not cast value of type 'NSNull' (0x...) to 'test_plugin.AllNullableTypes' (0x...).
  • dart test and flutter_plugin_tools format pass locally.

Fixes flutter/flutter#191254

Pre-Review Checklist

@google-cla

google-cla Bot commented Aug 21, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request updates Pigeon to version 27.3.3. It modifies the Swift generator to check if a Flutter API response is nil or NSNull when validating non-nullable return values, preventing crashes when FlutterStandardReader substitutes NSNull for nil. It also updates the generated Swift files, adds a regression test in NullableReturnsTests.swift, and adds a generator unit test in swift_generator_test.dart. There are no review comments, so no further feedback is provided.

@Xelorium
Xelorium force-pushed the pigeon-swift-nsnull-null-check branch from fbfef09 to 274fd02 Compare August 21, 2026 10:58
`FlutterStandardReader` substitutes `NSNull` for a `nil` element of a
list, so a null reply for a non-null return value arrives as `NSNull`
rather than as `nil`. The generated `listResponse[0] == nil` check did
not catch that, and the following force cast aborted the process.

In `webview_flutter_wkwebview` this crashes apps whenever the native
instance manager cannot resolve the returned instance, which the plugin
itself causes by clearing the manager on scene disconnect and app
termination while an authentication challenge is in flight.
@Xelorium
Xelorium force-pushed the pigeon-swift-nsnull-null-check branch from 274fd02 to a3cd890 Compare August 25, 2026 08:09
@Xelorium

Copy link
Copy Markdown
Contributor Author

Thanks for the pointer on flutter/flutter#191254 — opened this as suggested there.

Small status note in case it helps whoever picks it up: the branch is rebased onto the 28.0.0 async change, so it is mergeable again, and the CLA check is green. The test matrix hasn't run yet since the CICD label isn't something I can add myself.

No rush — happy to make any changes you'd like when you get a chance to look.

@Piinks
Piinks requested a review from bparrishMines August 25, 2026 20:41

@bparrishMines bparrishMines left a comment

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.

Thanks for the contribution!

I think this may be the true cause for flutter/flutter#162437. Thanks for debugging this.

@tarrinneal Where are you with the FFI/JNI integration? Can this land without disrupting #11352?

Comment on lines +67 to +80
@Test
func nullReplyForNonNullReturnFailsWithoutCrashing() async throws {
let binaryMessenger = MockBinaryMessenger<NSNull>(codec: codec)
binaryMessenger.result = NSNull()
let api = FlutterIntegrationCoreApi(binaryMessenger: binaryMessenger)

do {
// `sendMultipleNullableTypes` has a non-null return value.
_ = try await api.sendMultipleNullableTypes(aBool: nil, anInt: nil, aString: nil)
Issue.record("Expected a null-error but the call succeeded.")
} catch let error as PigeonError {
#expect(error.code == "null-error")
}
}

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.

This should be moved in to the NullableReturnsTests or move this class to a separate file.

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.

Moving into the NullableReturnsTests class would be preferred.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Moved into the NullableReturnsTests.

@bparrishMines bparrishMines added the CICD Run CI/CD label Sep 2, 2026
@tarrinneal

Copy link
Copy Markdown
Contributor

@tarrinneal Where are you with the FFI/JNI integration? Can this land without disrupting #11352?

I actually already added logic to fix this in the ffi pr. If I haven't already changed it to solve this problem, I can.

@tarrinneal

Copy link
Copy Markdown
Contributor

This pr can land first, it doesn't really matter

Comment on lines +1754 to +1756
// `FlutterStandardReader` substitutes `NSNull` for a `nil` element of
// a list, so a null reply can arrive as either. See
// https://github.com/flutter/flutter/issues/191254.

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.

this isn't needed

Comment on lines +1175 to +1177
// `FlutterStandardReader` substitutes `NSNull` for a `nil` element of a
// list, so both need to be treated as a null reply before the value is
// cast. See https://github.com/flutter/flutter/issues/191254.

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.

this isn't needed either

@tarrinneal

Copy link
Copy Markdown
Contributor

Make sure to run format and analyze

Comment on lines +57 to +68
/// Regression test for https://github.com/flutter/flutter/issues/191254.
///
/// `FlutterStandardReader` substitutes `NSNull` for a `nil` element of a list,
/// so a null reply for a non-null return value arrives as `NSNull` rather than
/// as `nil`. Before the fix that value was force-cast to the return type, which
/// aborted the process instead of reporting an error.
@MainActor
struct NullReplyForNonNullReturnTests {
let codec = FlutterStandardMessageCodec.sharedInstance()

@Test
func nullReplyForNonNullReturnFailsWithoutCrashing() async throws {

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.

the comments aren't needed if the test name is something like nonNullReturnFailsOnNSNullResponse.

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Sep 3, 2026
@Xelorium

Xelorium commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews! Addressed all the comments: dropped the explanatory comments in the generator and the generator test, and folded the regression test into NullableReturnsTests as nonNullReturnFailsOnNSNullResponse. Ran format, analyze and the Dart + iOS unit tests locally, all green.

@bparrishMines bparrishMines left a comment

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.

LGTM

@bparrishMines bparrishMines added the CICD Run CI/CD label Sep 4, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Sep 14, 2026
@bparrishMines bparrishMines added the CICD Run CI/CD label Sep 14, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Sep 14, 2026
@bparrishMines bparrishMines added the CICD Run CI/CD label Sep 14, 2026
@bparrishMines bparrishMines added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 15, 2026
@auto-submit
auto-submit Bot merged commit d8aba48 into flutter:main Sep 15, 2026
14 checks passed
GhagSagar23 pushed a commit to Kryonex-Labs/flutter that referenced this pull request Sep 16, 2026
…r#192876)

flutter/packages@bebbb57...c2b58e1

2026-09-15 stuartmorgan@google.com [local_auth] Convert implementation
to Kotlin (flutter/packages#12635)
2026-09-15 mhmmd.bayraktar@gmail.com [pigeon] Treat NSNull as null for
non-null Flutter API returns (flutter/packages#12531)
2026-09-15 daniel.leon@cloudsufi.com [espresso] Adopt code-excerpts for
README (flutter/packages#12644)
2026-09-15 daniel.leon@cloudsufi.com [pointer_interceptor] Adopt
code-excerpts for README (flutter/packages#12641)
2026-09-15 47866232+chunhtai@users.noreply.github.com [ci] Checks
unmerged batch release sync back before next release
(flutter/packages#12868)
2026-09-15 puneetkukreja98@gmail.com [material_ui] Add sortIconBuilder
to DataTable, PaginatedDataTable, and DataTableThemeData
(flutter/packages#12645)
2026-09-15 10687576+bparrishMines@users.noreply.github.com
[cross_file_platform_interface] Platform interface implementation of
`cross_file` (flutter/packages#12825)
2026-09-15 10687576+bparrishMines@users.noreply.github.com [cross_file]
Move cross_file into a federated cross_file directory
(flutter/packages#12869)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Xelorium added a commit to Xelorium/packages that referenced this pull request Sep 18, 2026
Picks up the Swift generator fix from
flutter#12531, so a
`WKNavigationDelegate.didReceiveAuthenticationChallenge` reply that the
native instance manager can no longer resolve reports a `null-error`
instead of force-casting `NSNull` and aborting the process.
auto-submit Bot pushed a commit that referenced this pull request Sep 23, 2026
follow up to #12531 just to keep code cleaner (while it's on top of my mind)
auto-submit Bot pushed a commit that referenced this pull request Sep 29, 2026
## Description

Regenerates `webview_flutter_wkwebview` with pigeon `29.0.2`, which contains the Swift generator fix from #12531.

The generated `WKNavigationDelegate.didReceiveAuthenticationChallenge` reply handler now treats `NSNull` as a null reply, so a response the native `PigeonInstanceManager` can no longer resolve reports a `null-error` instead of force-casting and aborting the process:

```
Could not cast value of type 'NSNull' (0x...) to 'webview_flutter_wkwebview.AuthenticationChallengeResponse' (0x...).
```

The diff is large because the package was last generated with `27.3.2`, so it also picks up the `28.x`/`29.x` generator changes; no hand-written code needed updating.

## Verification

* `flutter analyze` is clean.
* All 154 unit tests pass.
* The iOS example builds for the simulator.
* The `NSNull` check is present at the `AuthenticationChallengeResponse` call site in the regenerated `WebKitLibrary.g.swift`.

*Fixes flutter/flutter#191254

## Pre-Review Checklist
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD p: pigeon platform-ios platform-macos

Projects

None yet

4 participants