From ec954063051dbac6fa0d3593598a28ae77ba6bcb Mon Sep 17 00:00:00 2001 From: billisdead Date: Mon, 22 Jun 2026 13:10:01 +0200 Subject: [PATCH] debug: add crash boundary and global error handler to surface release crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catches React render errors (ErrorBoundary) and fatal JS errors (ErrorUtils.setGlobalHandler) and displays the stacktrace on-screen instead of crashing to desktop. Temporary — remove after crash identified. Co-Authored-By: Claude Sonnet 4.6 --- app/_layout.tsx | 97 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 19 deletions(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index 2e56cf7..1892cf6 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,13 +1,55 @@ -import { useEffect } from "react"; +import { useEffect, useState, Component } from "react"; +import type { ReactNode } from "react"; import { Stack, router } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { View, Text, ScrollView } from "react-native"; import "../global.css"; import { Colors } from "@/constants/theme"; import { useSettings } from "@/hooks/useSettings"; +// Catch JS errors in release mode and show them on-screen instead of +// silently crashing to desktop. Remove once the crash is identified. +class CrashBoundary extends Component< + { children: ReactNode }, + { error: Error | null } +> { + state = { error: null }; + static getDerivedStateFromError(e: Error) { + return { error: e }; + } + render() { + const { error } = this.state; + if (error) { + return ( + + + CRASH — copie ce texte + + + + {String(error)}{"\n\n"}{(error as any).stack} + + + + ); + } + return this.props.children; + } +} + export default function RootLayout() { const { settings, ready } = useSettings(); + const [jsError, setJsError] = useState(null); + + useEffect(() => { + const prev = (ErrorUtils as any).getGlobalHandler(); + (ErrorUtils as any).setGlobalHandler((e: Error, isFatal: boolean) => { + if (isFatal) setJsError(`${String(e)}\n\n${(e as any).stack ?? ""}`); + prev?.(e, isFatal); + }); + return () => (ErrorUtils as any).setGlobalHandler(prev); + }, []); useEffect(() => { if (!ready) return; @@ -16,24 +58,41 @@ export default function RootLayout() { } }, [ready, settings.onboardingDone]); + if (jsError) { + return ( + + + FATAL JS ERROR — copie ce texte + + + + {jsError} + + + + ); + } + return ( - - - - - - - - - - + + + + + + + + + + + + ); }