From 26d34e136d844919f1bcd04ccdea2f5ea3d765ac Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Tue, 9 Jun 2026 15:07:03 -0700 Subject: [PATCH 1/3] fix(android): don't crash in Fragment.onCreateView on stale-fragment race onCreateView already returns null when its entry, resolvedPage, or frame is missing, but it first called Trace.error, which rethrows through the registered error handler across the JNI boundary and turns the recoverable case into a fatal "Calling js method onCreateView failed" crash. These nulls happen when the FragmentManager drives a stale fragment to CREATE_VIEW after NativeScript has already torn down the corresponding backstack entry (process death / activity recreation after rotation or theme change, clearHistory, or rapid navigate+goBack). The fragment should just be discarded, not crash the app. Downgrade the three guards to Trace.messageType.warn so the existing return-null recovery path is actually reached, consistent with the existing onDestroy handling of the same race. --- .../core/ui/frame/frame-helper-for-android.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/core/ui/frame/frame-helper-for-android.ts b/packages/core/ui/frame/frame-helper-for-android.ts index d50236d4e7..63281c0ae0 100644 --- a/packages/core/ui/frame/frame-helper-for-android.ts +++ b/packages/core/ui/frame/frame-helper-for-android.ts @@ -112,21 +112,33 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks const entry = this.entry; if (!entry) { - Trace.error(`${fragment}.onCreateView: entry is null or undefined`); + // Recoverable race: a stale fragment is being driven to onCreateView by the + // FragmentManager after its entry was cleared. + // Using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary. + if (Trace.isEnabled()) { + Trace.write(`${fragment}.onCreateView: entry is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.warn); + } return null; } const page = entry.resolvedPage; if (!page) { - Trace.error(`${fragment}.onCreateView: entry has no resolvedPage`); + // Logging via Trace.error (routes to an error handler, which throws) here is fatal because the registered error handler rethrows across the JNI boundary. + if (Trace.isEnabled()) { + Trace.write(`${fragment}.onCreateView: entry has no resolvedPage`, Trace.categories.NativeLifecycle, Trace.messageType.warn); + } return null; } const frame = this.frame; if (!frame) { - Trace.error(`${fragment}.onCreateView: this.frame is null or undefined`); + // Recoverable race: the owning frame was already torn down. Returning null discards + // this fragment; using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary. + if (Trace.isEnabled()) { + Trace.write(`${fragment}.onCreateView: this.frame is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.warn); + } return null; } From 2b5167404282a899f90bd699b6eeedd58bf756d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 02:11:03 +0000 Subject: [PATCH 2/3] fix(android): always log onCreateView stale fragment guards as errors --- packages/core/ui/frame/frame-helper-for-android.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/core/ui/frame/frame-helper-for-android.ts b/packages/core/ui/frame/frame-helper-for-android.ts index 63281c0ae0..17e58f4688 100644 --- a/packages/core/ui/frame/frame-helper-for-android.ts +++ b/packages/core/ui/frame/frame-helper-for-android.ts @@ -115,9 +115,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks // Recoverable race: a stale fragment is being driven to onCreateView by the // FragmentManager after its entry was cleared. // Using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary. - if (Trace.isEnabled()) { - Trace.write(`${fragment}.onCreateView: entry is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.warn); - } + Trace.write(`${fragment}.onCreateView: entry is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.error); return null; } @@ -125,9 +123,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks const page = entry.resolvedPage; if (!page) { // Logging via Trace.error (routes to an error handler, which throws) here is fatal because the registered error handler rethrows across the JNI boundary. - if (Trace.isEnabled()) { - Trace.write(`${fragment}.onCreateView: entry has no resolvedPage`, Trace.categories.NativeLifecycle, Trace.messageType.warn); - } + Trace.write(`${fragment}.onCreateView: entry has no resolvedPage`, Trace.categories.NativeLifecycle, Trace.messageType.error); return null; } @@ -136,9 +132,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks if (!frame) { // Recoverable race: the owning frame was already torn down. Returning null discards // this fragment; using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary. - if (Trace.isEnabled()) { - Trace.write(`${fragment}.onCreateView: this.frame is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.warn); - } + Trace.write(`${fragment}.onCreateView: this.frame is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.error); return null; } From 11902bc3f702449b5c01e02d9d6875980e900f5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 02:14:35 +0000 Subject: [PATCH 3/3] docs(android): clarify Trace.write error logging remains non-fatal --- packages/core/ui/frame/frame-helper-for-android.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/ui/frame/frame-helper-for-android.ts b/packages/core/ui/frame/frame-helper-for-android.ts index 17e58f4688..093dac0163 100644 --- a/packages/core/ui/frame/frame-helper-for-android.ts +++ b/packages/core/ui/frame/frame-helper-for-android.ts @@ -115,6 +115,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks // Recoverable race: a stale fragment is being driven to onCreateView by the // FragmentManager after its entry was cleared. // Using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary. + // Trace.write(..., Trace.messageType.error) logs without routing through that throwing path. Trace.write(`${fragment}.onCreateView: entry is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.error); return null; @@ -123,6 +124,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks const page = entry.resolvedPage; if (!page) { // Logging via Trace.error (routes to an error handler, which throws) here is fatal because the registered error handler rethrows across the JNI boundary. + // Trace.write(..., Trace.messageType.error) keeps this recoverable path non-fatal. Trace.write(`${fragment}.onCreateView: entry has no resolvedPage`, Trace.categories.NativeLifecycle, Trace.messageType.error); return null; @@ -132,6 +134,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks if (!frame) { // Recoverable race: the owning frame was already torn down. Returning null discards // this fragment; using Trace.error (routes to an error handler, which throws) would be fatal as the error handler rethrows across the JNI boundary. + // Trace.write(..., Trace.messageType.error) logs without invoking the throwing error handler. Trace.write(`${fragment}.onCreateView: this.frame is null or undefined`, Trace.categories.NativeLifecycle, Trace.messageType.error); return null;