You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[webview_flutter_wkwebview] Crash: Could not cast value of type 'NSNull' to 'AuthenticationChallengeResponse' when the instance manager is cleared while an auth challenge is in flight #191254
Create an app with a scene manifest (UIApplicationSceneManifest in Info.plist, default for recent Xcode templates).
Show a WebViewWidget loading any https page, so WKNavigationDelegate.didReceiveAuthenticationChallenge round-trips to Dart for the server-trust challenge.
While a page load is in progress, background the app so iOS disconnects the scene (or let the app terminate).
Expected results
No crash. A late or unresolvable authentication-challenge reply should fall back to performDefaultHandling (or surface a Pigeon error), as the null-return branch clearly intends.
Actual results
The app crashes with:
Could not cast value of type 'NSNull' (0x...) to 'webview_flutter_wkwebview.AuthenticationChallengeResponse' (0x...).
Analysis
WebViewFlutterPlugin.tearDownProxyAPIRegistrar() (called from sceneDidDisconnect and applicationWillTerminate) calls instanceManager.removeAllObjects(). A didReceiveAuthenticationChallenge reply that is already in flight then carries an
identifier that no longer resolves:
WebKitLibrary.g.swift (3.26.0), codec reader:
case128:letidentifier=self.readValue()letinstance:AnyObject?=pigeonRegistrar.instanceManager.instance(forIdentifier:...)ifinstance==nil{print("Failed to find instance with identifier: \(identifier!)")}return instance
FlutterStandardReader substitutes NSNull for a nil element when building the reply list, so the existing guard in didReceiveAuthenticationChallenge does not catch it:
}elseiflistResponse[0]==nil{ // NSNull is not nil -> guard passes
...}else{letresult=listResponse[0]as!AuthenticationChallengeResponse // <-- crash
completion(.success(result))}
The same as! pattern after an == nil guard is used for every non-null Pigeon return value in the generated file, so this is
a general Pigeon codegen issue, not specific to the auth-challenge callback — but the auth challenge is the easiest one to hit
in production because it fires on ordinary https loads.
Suggested fix
Either treat NSNull as null in the generated guard (listResponse[0] == nil || listResponse[0] is NSNull), or use a safe
cast with an error completion instead of as!. Additionally, didReceiveAuthenticationChallenge could complete with performDefaultHandling when the response cannot be resolved after teardown.
Versions
webview_flutter_wkwebview 3.26.0 (latest at time of report), webview_flutter 4.14.1
@bayraktarmdkaraca Thanks for the detailed report and analysis. Could you please provide a minimal reproducible example that demonstrates the crash? Additionally, could you please verify whether the issue is still reproducible on the latest stable Flutter version? and also provide fluter doctor -v output. This will help us confirm the issue and provide a reliable reproduction for further investigation.
Thanks for looking at this. Minimal reproduction, verification on the latest stable, and flutter doctor -v below.
Reproduction
The crash happens whenever Dart answers WKNavigationDelegate.didReceiveAuthenticationChallenge with an AuthenticationChallengeResponse whose identifier the nativePigeonInstanceManager cannot resolve: the codec reader returns nil, FlutterStandardReader substitutes NSNull, the listResponse[0] == nil guard in WebKitLibrary.g.swift does not catch NSNull, and the following as! aborts the process.
In production that unresolvable identifier is produced by the plugin itself — WebViewFlutterPlugin.tearDownProxyAPIRegistrar() calls instanceManager.removeAllObjects(), and iOS triggers it through sceneDidDisconnect / applicationWillTerminate while a challenge is still in flight. That is a race and hard to hit on demand, so the sample below puts the app in the same state deterministically through PigeonOverrides.authenticationChallengeResponse_createAsync. Everything else — the real WKWebView, a real httpBasic challenge and the plugin's own didReceiveAuthenticationChallenge implementation — is untouched.
Setup:
flutter create --platforms=ios wkwebview_auth_crash
cd wkwebview_auth_crash
flutter pub add webview_flutter webview_flutter_wkwebview
# replace lib/main.dart with the file below# add NSAppTransportSecurity / NSAllowsLocalNetworking = true to ios/Runner/Info.plist# (only needed for the local http://127.0.0.1 server that raises the challenge)
flutter run
The app aborts as soon as the local server asks for credentials — no interaction needed.
lib/main.dart
// Minimal reproduction of the force-cast crash in webview_flutter_wkwebview://// Could not cast value of type 'NSNull' (0x...) to// 'webview_flutter_wkwebview.AuthenticationChallengeResponse' (0x...).//// The crash happens when Dart answers `WKNavigationDelegate.// didReceiveAuthenticationChallenge` with an `AuthenticationChallengeResponse`// whose identifier the *native* `PigeonInstanceManager` cannot resolve.//// In production that state is produced by the plugin itself:// `WebViewFlutterPlugin.tearDownProxyAPIRegistrar()` calls// `instanceManager.removeAllObjects()`, and iOS triggers it through// `sceneDidDisconnect` / `applicationWillTerminate` while a challenge is still// in flight. That is a race and therefore awkward to reproduce on demand, so// this sample puts the app in the same state deterministically: the response// object exists in the Dart instance manager but not in the native one.//// Everything else is the plugin's own code path: a real WKWebView, a real// httpBasic challenge, and the plugin's own `didReceiveAuthenticationChallenge`// implementation.//// Run on an iOS device or simulator://// flutter run//// The app crashes with SIGABRT as soon as the local server asks for// credentials. Expected instead: a Pigeon error (or a fallback to// `performDefaultHandling`), not a hard crash.import'dart:async';
import'dart:io';
import'package:flutter/material.dart';
import'package:webview_flutter/webview_flutter.dart';
// ignore: implementation_importsimport'package:webview_flutter_wkwebview/src/common/web_kit.g.dart';
voidmain() {
// Hand the plugin a response that the native instance manager does not know// about - the exact state `removeAllObjects()` leaves the app in.PigeonOverrides.authenticationChallengeResponse_createAsync = (
UrlSessionAuthChallengeDisposition disposition,
URLCredential? credential,
) async {
final response =AuthenticationChallengeResponse.pigeon_detached(
disposition: disposition,
credential: credential,
);
PigeonInstanceManager.instance.addDartCreatedInstance(response);
return response;
};
runApp(constReproApp());
}
classReproAppextendsStatelessWidget {
constReproApp({super.key});
@overrideWidgetbuild(BuildContext context) =>constMaterialApp(home:ReproPage());
}
classReproPageextendsStatefulWidget {
constReproPage({super.key});
@overrideState<ReproPage> createState() =>_ReproPageState();
}
class_ReproPageStateextendsState<ReproPage> {
WebViewController? _controller;
String _status ='Starting the local server...';
@overridevoidinitState() {
super.initState();
_start();
}
Future<void> _start() async {
finalint port =await_startBasicAuthServer();
final controller =WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onHttpAuthRequest: (HttpAuthRequest request) {
setState(() => _status ='Auth challenge received, answering it -> crash');
request.onProceed(constWebViewCredential(user:'user', password:'password'));
},
),
);
await controller.loadRequest(Uri.parse('http://127.0.0.1:$port/'));
setState(() {
_controller = controller;
_status ='Loading http://127.0.0.1:$port/';
});
}
/// Always answers 401 so WKWebView raises an httpBasic challenge.Future<int> _startBasicAuthServer() async {
finalHttpServer server =awaitHttpServer.bind(InternetAddress.loopbackIPv4, 0);
server.listen((HttpRequest request) async {
request.response
..statusCode =HttpStatus.unauthorized
..headers.set(HttpHeaders.wwwAuthenticateHeader, 'Basic realm="repro"')
..write('unauthorized');
await request.response.close();
});
return server.port;
}
@overrideWidgetbuild(BuildContext context) {
finalWebViewController? controller = _controller;
returnScaffold(
appBar:AppBar(title:constText('wkwebview auth challenge crash')),
body:Column(
children:<Widget>[
Padding(padding:constEdgeInsets.all(16), child:Text(_status)),
Expanded(
child: controller ==null?constCenter(child:CircularProgressIndicator())
:WebViewWidget(controller: controller),
),
],
),
);
}
}
Actual result
Could not cast value of type 'NSNull' (0x1150485b8) to 'webview_flutter_wkwebview.AuthenticationChallengeResponse' (0x102d7eed0).
Lost connection to device.
Expected instead: a Pigeon error, or a fallback to performDefaultHandling — the else if listResponse[0] == nil branch right above the as! shows the intent, it just does not recognise NSNull.
The production crashes we originally reported come from Flutter 3.44.9 with the same plugin version.
Note the same message was reported in #162437 and fixed in 3.18.3 by introducing AuthenticationChallengeResponse.createAsync. That closed the "response was never registered natively" case, but not this one: the identifier is registered and then dropped natively, and the NSNull decode still force-casts. The same == nil guard followed by as! is emitted for every non-null return value in WebKitLibrary.g.swift, so AuthenticationChallengeResponse is just the easiest one to hit in the wild (it fires on ordinary https loads).
flutter doctor -v
Doctor output (Flutter 3.47.0, used for the reproduction)
[✓] Flutter (Channel stable, 3.47.0, on macOS 26.5.2 25F84 darwin-arm64 (Rosetta), locale en-TR) [2.1s] • Flutter version 3.47.0 on channel stable at /Users/dev/sdks/flutter_3.47.0 • Upstream repository https://github.com/flutter/flutter.git • Framework revision 4cf2416426 (8 days ago), 2026-08-11 11:53:49 -0700 • Engine revision 5f77625673 • Dart version 3.13.0 • DevTools version 2.60.0 • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [3.8s] • Android SDK at /Users/dev/Library/Android/sdk • Emulator version 37.1.11.0 (build_id 15917651) (CL:N/A) • Platform android-36.1, build-tools 36.1.0 • Java binary at: /Users/dev/Library/Java/JavaVirtualMachines/temurin-24.0.2/Contents/Home/bin/java This JDK is specified in your Flutter configuration. To change the current JDK, run: `flutter config --jdk-dir="path/to/jdk"`. • Java version OpenJDK Runtime Environment Temurin-24.0.2+12 (build 24.0.2+12) • All Android licenses accepted.[✓] Xcode - develop for iOS and macOS (Xcode 26.6) [1,624ms] • Xcode at /Applications/Xcode.app/Contents/Developer • Build 17F113 • CocoaPods version 1.16.2[✓] Chrome - develop for the web [25ms] • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome[✓] Connected device (3 available) [44.1s] • iPhone 17 Pro (mobile) • 23F1B535-629A-4043-AC94-7D3EB773CF90 • ios • com.apple.CoreSimulator.SimRuntime.iOS-26-5 (simulator) • macOS (desktop) • macos • darwin-arm64 • macOS 26.5.2 25F84 darwin-arm64 (Rosetta) • Chrome (web) • chrome • web-javascript • Google Chrome 151.0.7922.138[✓] Network resources [806ms] • All expected network resources are available.• No issues found!
@bayraktarmdkaraca Thanks for providing the minimal reproduction, crash stack, and flutter doctor -v output. I’ve reviewed the additional information and am routing this issue to the team-ios for further investigation.
29.0.2 isn't on pub.dev yet, so this can't land as a PR quite yet. I have the regeneration prepared and verified locally in the meantime — analyze clean, all 154 unit tests pass, the iOS example builds, and the didReceiveAuthenticationChallenge reply now goes through the NSNull check — though the diff is large since it regenerates from 27.3.2. Is updating the generated code something the team handles as part of the pigeon release, or would a PR from me be useful once it publishes?
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.
Activity
suryaKommana2662 commentedon Aug 18, 2026
@bayraktarmdkaraca Thanks for the detailed report and analysis. Could you please provide a minimal reproducible example that demonstrates the crash? Additionally, could you please verify whether the issue is still reproducible on the latest stable Flutter version? and also provide
fluter doctor -voutput. This will help us confirm the issue and provide a reliable reproduction for further investigation.bayraktarmdkaraca commentedon Aug 19, 2026
Thanks for looking at this. Minimal reproduction, verification on the latest stable, and
flutter doctor -vbelow.Reproduction
The crash happens whenever Dart answers
WKNavigationDelegate.didReceiveAuthenticationChallengewith anAuthenticationChallengeResponsewhose identifier the nativePigeonInstanceManagercannot resolve: the codec reader returnsnil,FlutterStandardReadersubstitutesNSNull, thelistResponse[0] == nilguard inWebKitLibrary.g.swiftdoes not catchNSNull, and the followingas!aborts the process.In production that unresolvable identifier is produced by the plugin itself —
WebViewFlutterPlugin.tearDownProxyAPIRegistrar()callsinstanceManager.removeAllObjects(), and iOS triggers it throughsceneDidDisconnect/applicationWillTerminatewhile a challenge is still in flight. That is a race and hard to hit on demand, so the sample below puts the app in the same state deterministically throughPigeonOverrides.authenticationChallengeResponse_createAsync. Everything else — the realWKWebView, a real httpBasic challenge and the plugin's owndidReceiveAuthenticationChallengeimplementation — is untouched.Setup:
The app aborts as soon as the local server asks for credentials — no interaction needed.
lib/main.dart
Actual result
Crashing thread (
Runner-2026-08-19-093456.ips,EXC_CRASH / SIGABRT):Expected instead: a Pigeon error, or a fallback to
performDefaultHandling— theelse if listResponse[0] == nilbranch right above theas!shows the intent, it just does not recogniseNSNull.Version verification
Reproducible on the latest stable:
4cf2416426, engine5f77625673),webview_flutter 4.14.1/webview_flutter_wkwebview 3.26.0, iOS 26.5 simulator (iPhone 17 Pro) and physical devices — sample above, crashes every run.Note the same message was reported in #162437 and fixed in 3.18.3 by introducing
AuthenticationChallengeResponse.createAsync. That closed the "response was never registered natively" case, but not this one: the identifier is registered and then dropped natively, and theNSNulldecode still force-casts. The same== nilguard followed byas!is emitted for every non-null return value inWebKitLibrary.g.swift, soAuthenticationChallengeResponseis just the easiest one to hit in the wild (it fires on ordinary https loads).flutter doctor -v
Doctor output (Flutter 3.47.0, used for the reproduction)
suryaKommana2662 commentedon Aug 19, 2026
@bayraktarmdkaraca Thanks for providing the minimal reproduction, crash stack, and
flutter doctor -voutput. I’ve reviewed the additional information and am routing this issue to theteam-iosfor further investigation.hellohuanlin commentedon Aug 20, 2026
@bayraktarmdkaraca it looks like you have already done the hard part of finding the root cause. PRs welcome.
4 remaining items
bparrishMines commentedon Sep 15, 2026
The
webview_flutter_wkwebviewcode generation still needs to be updated.[pigeon] Treat NSNull as null for non-null Flutter API returns (flutt…
AbdeMohlbi commentedon Sep 16, 2026
huh smth is wrong i did update the main branch on my fork and this caused the issue to get closed ?
Xelorium commentedon Sep 16, 2026
29.0.2isn't on pub.dev yet, so this can't land as a PR quite yet. I have the regeneration prepared and verified locally in the meantime — analyze clean, all 154 unit tests pass, the iOS example builds, and thedidReceiveAuthenticationChallengereply now goes through theNSNullcheck — though the diff is large since it regenerates from 27.3.2. Is updating the generated code something the team handles as part of the pigeon release, or would a PR from me be useful once it publishes?bparrishMines commentedon Sep 16, 2026
It needed to publish manually due to #192709. It should be on pub now. @Xelorium its fine for you to generate it if you already have the PR ready.
[webview_flutter_wkwebview] Regenerate with pigeon 29.0.4 (flutter#12923
github-actions commentedon Oct 13, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.