Manejadores de Errores Globales
ErrorUtils.setGlobalHandler y promesas no controladas.
Busca en todas las páginas de la documentación
ErrorUtils.setGlobalHandler y promesas no controladas.
Los límites de errores de React solo cubren el árbol de renderizado. ErrorUtils.setGlobalHandler es el hook de React Native para errores JavaScript no capturados - incluyendo muchos fallos que nunca tocan un límite. Emparéjalo con un manejo disciplinado de async/await para que los rechazos de promesas no sean silenciosos en producción.
Tarjeta de referencia rápida - lista para copiar y pegar.
// src/bootstrap/installGlobalErrorHandlers.ts
// Importa esto una sola vez desde el punto de entrada de tu app (index.js / _layout.tsx) antes que otro código.
type GlobalHandler = (error: Error, isFatal?: boolean) => void;
declare const ErrorUtils: {
getGlobalHandler(): GlobalHandler;
setGlobalHandler(handler: GlobalHandler): void;
};
export function installGlobalErrorHandlers(report: (error: Error, meta: { isFatal: boolean }) => void) {
const previous = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler((error, isFatal = false) => {
report(error, { isFatal });
previous(error, isFatal);
});
}// app/_layout.tsx (raíz de Expo Router) - llama antes de renderizar proveedores
import { installGlobalErrorHandlers } from "@/bootstrap/installGlobalErrorHandlers";
import { Stack } from "expo-router";
installGlobalErrorHandlers((error, { isFatal }) => {
console.error("[global]", isFatal ? "FATAL" : "no fatal", error);
// crashReporter.captureException(error, { extra: { isFatal } });
});
export default function RootLayout() {
return <Stack />;
}Cuándo usarlo:
isFatal, canal de compilación, ruta) en cada reporte de fallos.// installGlobalErrorHandlers.ts - módulo de bootstrap completo
type GlobalHandler = (error: Error, isFatal?: boolean) => void;
declare const ErrorUtils: {
getGlobalHandler(): GlobalHandler;
setGlobalHandler(handler: GlobalHandler): void;
};
export type ErrorReport = {
message: string;
stack?: string;
isFatal: boolean;
kind: "global" | "unhandled-rejection";
};
const reports: ErrorReport[] = [];
export function getCapturedReports() {
return [...reports];
}
function normalizeError(reason: unknown): Error {
if (reason instanceof Error) return reason;
return new Error(typeof reason === "string" ? reason : JSON.stringify(reason));
}
function record(error: Error, meta: { isFatal: boolean; kind: ErrorReport["kind"] }) {
reports.push({
message: error.message,
stack: error.stack,
isFatal: meta.isFatal,
kind: meta.kind,
});
console.error(`[${meta.kind}]`, meta.isFatal ? "FATAL" : "no fatal", error.message);
}
export function installGlobalErrorHandlers() {
const previous = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler((error, isFatal = false) => {
record(error, { isFatal, kind: "global" });
previous(error, isFatal);
});
// Cinturón y tirantes: logging explícito de rechazo no controlado (Hermes / RN dev tooling también pueden enrutar estos)
const g = globalThis as typeof globalThis & {
onunhandledrejection?: (event: { reason?: unknown }) => void;
};
g.onunhandledrejection = (event) => {
const error = normalizeError(event.reason);
record(error, { isFatal: false, kind: "unhandled-rejection" });
};
}
// --- Pantallas de demostración ---
import { useEffect, useState } from "react";
import { Pressable, ScrollView, StyleSheet, Text, View } from "react-native";
installGlobalErrorHandlers();
function causeUnhandledRejection() {
void Promise.reject(new Error("sincronización de facturación falló"));
}
function causeThrowInTimer() {
setTimeout(() => {
throw new Error("temporizador lanzado fuera de React");
}, 0);
}
export default function App() {
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick((t) => t + 1), 1000);
return () => clearInterval(id);
}, []);
const latest = getCapturedReports().slice(-3);
return (
<ScrollView contentContainerStyle={styles.container}>
<Text style={styles.title}>Manejadores de Errores Globales</Text>
<Text style={styles.sub}>Reloj: {tick}s (app aún ejecutándose)</Text>
<Pressable style={styles.btn} onPress={causeUnhandledRejection}>
<Text style={styles.btnLabel}>Desencadenar rechazo no controlado</Text>
</Pressable>
<Pressable style={styles.btn} onPress={causeThrowInTimer}>
<Text style={styles.btnLabel}>Desencadenar lanzamiento de temporizador</Text>
</Pressable>
<Text style={styles.section}>Últimos reportes capturados</Text>
{latest.length === 0 && <Text style={styles.meta}>Ninguno aún - toca un botón arriba.</Text>}
{latest.map((r, i) => (
<View key={`${r.message}-${i}`} style={styles.card}>
<Text style={styles.cardTitle}>
[{r.kind}] {r.isFatal ? "FATAL" : "no fatal"}
</Text>
<Text style={styles.cardBody}>{r.message}</Text>
</View>
))}
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { padding: 20, gap: 12 },
title: { fontSize: 24, fontWeight: "800" },
sub: { color: "#64748b" },
section: { fontSize: 16, fontWeight: "700", marginTop: 8 },
meta: { color: "#94a3b8" },
btn: { backgroundColor: "#2563eb", padding: 14, borderRadius: 8 },
btnLabel: { color: "#fff", fontWeight: "600", textAlign: "center" },
card: { backgroundColor: "#f1f5f9", padding: 12, borderRadius: 8 },
cardTitle: { fontWeight: "700", marginBottom: 4 },
cardBody: { color: "#334155" },
});Lo que esto demuestra:
ErrorUtils.setGlobalHandler instalado una sola vez en la carga del módulo antes de que se renderice la UI.isFatal se registra - la triaje de producción distingue entre paradas duras y errores suaves.onunhandledrejection captura fallos de promesas flotantes que nunca golpean un límite.ErrorUtils.setGlobalHandler(fn) reemplaza ese manejador. Tu función se ejecuta primero; típicamente registras, enriqueces, luego llamas al manejador anterior.isFatal sugiere si el tiempo de ejecución trata el error como terminal. Los fatales pueden terminar la ejecución saludable del paquete JS; siempre repórtalos inmediatamente..catch. En desarrollo, LogBox los muestra; en lanzamiento, el comportamiento depende de la versión de Hermes/RN - nunca confíes en la suerte._layout.tsx raíz) antes de que los enrutadores, análisis, y código de características carguen.| Método | Propósito |
|---|---|
getGlobalHandler() | Devuelve el manejador actualmente instalado (encadena esto) |
setGlobalHandler(fn) | Instala tu manejador para errores JS no capturados |
| Parámetro | Tipo | Descripción |
|---|---|---|
error | Error | El error JS lanzado o normalizado |
isFatal | boolean (opcional) | Cuando es true, el tiempo de ejecución considera el error terminal - prioriza el reporte de fallos |
setGlobalHandler y getGlobalHandler devuelven void / una función manejador respectivamente. No hay retorno de promesa - el manejador se ejecuta sincrónicamente en la ruta de fallos.
// app/_layout.tsx - inicio del archivo, antes de componentes
import { installGlobalErrorHandlers } from "@/bootstrap/installGlobalErrorHandlers";
import * as Sentry from "@sentry/react-native";
Sentry.init({ dsn: process.env.EXPO_PUBLIC_SENTRY_DSN });
installGlobalErrorHandlers((error, { isFatal }) => {
Sentry.captureException(error, { extra: { isFatal } });
});
export default function RootLayout() {
return <RootProviders />;
}installGlobalErrorHandlers| Enfoque | Rol |
|---|---|
.catch / try/catch en cada API async | Primario - previene que los rechazos se conviertan en globales |
onunhandledrejection en globalThis | Logger secundario en código de app |
Manejador global de ErrorUtils | Captura muchos lanzamientos no capturados; algunos rechazos se enrutan aquí en lanzamiento |
| SDK de reportero de fallos | A menudo instrumenta ambos automáticamente |
// Límite de API - prefiere corregir en la fuente
export async function fetchBalance(): Promise<number> {
try {
const res = await fetch("/api/balance");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = (await res.json()) as { balance: number };
return json.balance;
} catch (error) {
// Convierte a tipo Result o relanza a un estado de error de pantalla controlado
throw error instanceof Error ? error : new Error("fetching de saldo falló");
}
}
// Las tareas fire-and-forget aún deben manejar fallos
void syncAnalytics().catch((error) => {
console.warn("sincronización de análisis falló", error);
});| Capa | Captura | Experiencia de usuario |
|---|---|---|
| try/catch | Llamadas async/nativas conocidas | Error en línea, tostada, mensaje de formulario |
| Límite de error | Lanzamientos de renderizado/ciclo de vida | Fallback de pantalla con Reintentar |
| Global ErrorUtils | JS no capturado fuera de límites | Registrar/reportar; UI fatal predeterminada de RN en desarrollo |
| SDK nativo de fallos | Fallos nativos + JS | Panel de fallos, reproducción de sesión |
No renderices UI de React dentro de setGlobalHandler - el árbol puede ser inestable. Difiere UX de producto a límites y Copia de Error de Cara al Usuario.
type ReportFn = (error: Error, meta: { isFatal: boolean }) => void;
declare const ErrorUtils: {
getGlobalHandler(): (error: Error, isFatal?: boolean) => void;
setGlobalHandler(handler: (error: Error, isFatal?: boolean) => void): void;
};
export function installGlobalErrorHandlers(report: ReportFn): void {
const previous = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler((error, isFatal = false) => {
report(error, { isFatal: !!isFatal });
previous(error, isFatal);
});
}
function normalizeReason(reason: unknown): Error {
if (reason instanceof Error) return reason;
return new Error(String(reason));
}ErrorUtils es un global de React Native - no se importa desde react-native en todas las plantillas; decláralo una vez en un globals.d.ts si es necesario.Error antes de registrar para que los reporteros siempre reciban una forma de Error.Reemplazar el manejador global sin encadenar - El redbox de desarrollo y la plomería de fallos nativos dejan de funcionar. Corrección: Siempre llama a const prev = ErrorUtils.getGlobalHandler() e invoca prev(error, isFatal) después de reportar.
Registrar el manejador múltiples veces - Cada recarga en caliente o remontaje de diseño envuelve otra capa; reportes duplicados. Corrección: Protege con una bandera de módulo-nivel let installed = false.
Esperar que el manejador global muestre UI de pantalla amigable - React puede estar en medio de un renderizado o corrupto. Corrección: Reporta en el manejador; muestra UI de recuperación a través de límites de errores.
Llamadas void someAsync() no controladas - Las promesas flotantes evitan try/catch y límites. Corrección: void someAsync().catch(handle) o await dentro de un manejador de eventos con try/catch.
Ocultar fatales - Regresar temprano sin llamar al manejador anterior oculta errores catastróficos en desarrollo. Corrección: Reporta, luego siempre encadena a menos que poseas completamente el comportamiento de fallos en un cliente de desarrollo personalizado.
Instalar manejadores después de importaciones perezosas - Los errores durante la evaluación temprana del módulo se pierden tu manejador. Corrección: Importa bootstrap primero en index.js o el inicio de app/_layout.tsx.
Registro doble con Sentry - Sentry también parcha manejadores; el orden importa. Corrección: Sentry.init() primero, luego enriquecimiento personalizado que encadena, o usa los hooks de Sentry exclusivamente.
| Alternativa | Úsalo Cuando | No lo Uses Cuando |
|---|---|---|
ErrorUtils.setGlobalHandler | Endurecimiento de producción de RN base | Solo necesitas contención en tiempo de renderizado (usa límites) |
| SDK de reportero de fallos solo (Sentry) | Instrumentación administrada, simbolización | Necesitas registro local personalizado antes de inicialización de terceros |
| try/catch en todas partes | Flujos async predecibles | Lanzamientos de renderizado de componentes de terceros |
| Límites de errores de React | UI de fallback a nivel de pantalla | Errores de temporizador/nativos fuera de React |
| LogBox (solo desarrollo) | Depuración local | Captura de fallos de producción |
ErrorUtils es un objeto global proporcionado por el tiempo de ejecución de React Native con getGlobalHandler y setGlobalHandler. Es el hook de último recurso para errores JavaScript no capturados en el paquete JS.
En el inicio de app/_layout.tsx (importación de efecto secundario) o en index.js antes de registerRootComponent. Debe ejecutarse una vez, lo más temprano posible - antes de que navegación y módulos de características carguen.
Cuando es true, el tiempo de ejecución trata el error como terminal - el proceso o contexto JS puede no continuar confiablemente. Reporta inmediatamente y evita intentar UI de recuperación compleja desde el manejador global.
No - resuelven problemas diferentes. Los límites contienen errores de renderizado por pantalla. El manejador global captura errores no capturados fuera de la ruta de renderizado de React (temporizadores, algunos callbacks nativos, lanzamientos no capturados).
En desarrollo, LogBox advierte. En producción, el comportamiento varía - algunos rechazos se enrutan al manejador global, muchos no. Trata un .catch explícito en límites de API como obligatorio; usa onunhandledrejection como una red de seguridad secundaria.
Generalmente no - el árbol puede ser inestable y las alertas se apilan de manera molesta. Reporta el error y deja que los límites de errores o el siguiente lanzamiento de app presenten UX. Los fatales nativos pueden terminar la sesión de todas formas.
let installed = false;
export function installGlobalErrorHandlers() {
if (installed) return;
installed = true;
// setGlobalHandler...
}Protege la instalación en alcance de módulo en desarrollo.
Prefiere el ErrorUtils global con una declaración ambiental de TypeScript. Las plantillas varían; el tiempo de ejecución siempre expone el global en el hilo JS.
Llama a Sentry.init() primero, luego ya sea confía en el parche automático del manejador de Sentry o encadena tu enriquecimiento después de getGlobalHandler(). Ver sentry-expo / @sentry/react-native.
Errores capturados por try/catch, promesas manejadas, y errores contenidos por límites de errores de React (a menos que se relancen). Los fallos solo de nativos pueden evitar completamente los manejadores JS - necesitan SDKs nativos.
Raramente. Prefiere permitir que el usuario relance o navegue a inicio desde un fallback de límite. expo-updates reloadAsync() es un martillo pesado - reserva para flujos de recuperación OTA, no para cada error JS.
Desencadena un lanzamiento de temporizador o Promise.reject sin .catch como en el ejemplo funcional. Confirma que tus registros de reportero se ejecutan, luego verifica que el redbox de desarrollo aún aparece porque encadenaste el manejador anterior.
Hermes es el motor JS predeterminado en React Native 0.86 / Expo SDK 57. Los trazos de pila y el enrutamiento de rechazos de promesas difieren ligeramente de JSC, pero ErrorUtils sigue siendo el punto de integración estable.
error.message y stackisFatalEvita PII en metadatos de fallos globales.
Básicos cubre capas de try/catch, límites, y globales. Esta página implementa la capa global - ver Conceptos Básicos de Manejo de Errores para la imagen completa.
Versiones de Stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0, y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026