Back

Cómo manejar Uncaught (in promise) TypeError

Cómo manejar Uncaught (in promise) TypeError

Estás depurando tu aplicación frontend cuando la consola muestra: Uncaught (in promise) TypeError: Cannot read property 'type' of undefined. Este mensaje de error contiene dos problemas distintos que los desarrolladores suelen confundir. Comprender ambos es esencial para el manejo correcto de errores de Promises en JavaScript.

Este artículo explica qué significa realmente este error, por qué aparece y cómo manejar correctamente los rechazos de Promise no controlados en entornos de navegador.

Puntos clave

  • El mensaje “Uncaught (in promise) TypeError” señala dos problemas separados: un TypeError en tiempo de ejecución y un manejador de rechazo de Promise faltante.
  • Las causas comunes incluyen Promises flotantes (falta de await), manejadores .catch() olvidados y acceso a propiedades en undefined o null.
  • Envuelve las expresiones await en try/catch o adjunta .catch() al final de cada cadena de Promise para prevenir rechazos no controlados.
  • Usa encadenamiento opcional (?.) y fusión de nulos (??) para protegerte contra TypeErrors de propiedades indefinidas.
  • Reserva el evento unhandledrejection para monitoreo y telemetría, no como sustituto del manejo local de errores.

Qué significa realmente ‘Uncaught (in promise) TypeError’

Este mensaje de error comunica dos problemas separados:

  1. TypeError: Ocurrió un error en tiempo de ejecución, típicamente al acceder a una propiedad en undefined o null.
  2. Uncaught (in promise): La Promise que produjo este error no tenía un manejador de rechazo adjunto.

El navegador distingue estos casos porque los rechazos de Promise se comportan de manera diferente a los errores síncronos. Cuando ocurre un TypeError síncrono, la ejecución se detiene inmediatamente. Cuando el mismo error ocurre dentro de una Promise, el rechazo se propaga a través de la cadena de Promise. Si ningún .catch() o try/catch lo maneja, el navegador lo registra como un rechazo de Promise no controlado.

Para una referencia más profunda sobre el comportamiento de Promises y la propagación de errores, consulta la documentación de MDN sobre Promises.

Causas raíz comunes en código frontend

Errores lanzados dentro de funciones async

Cualquier excepción dentro de una función async rechaza automáticamente la Promise devuelta:

async function fetchUserData(userId) {
  const response = await fetch(`/api/users/${userId}`)
  const data = await response.json()
  return data.profile.name // TypeError si profile es undefined
}

fetchUserData(123) // Promise flotante—sin manejador adjunto

Palabras clave await faltantes

Olvidar await crea una Promise flotante que se ejecuta independientemente:

async function processData() {
  fetchUserData(123) // Falta await—el rechazo queda sin manejar
  console.log('Done')
}

Manejadores .catch() olvidados

Cadenas de Promise sin un bloque .catch() terminal dejan los rechazos sin manejar:

fetch('/api/data')
  .then(res => res.json())
  .then(data => data.items[0].name) // Sin .catch()—TypeError se propaga

Adjunción tardía de manejadores y temporización de microtareas

Los navegadores determinan si un rechazo está manejado después del punto de control de microtareas actual. Si un rechazo no tiene un manejador en ese punto, el navegador puede emitir un evento unhandledrejection y registrar una advertencia en la consola. Adjuntar un manejador poco después aún puede activar la advertencia en compilaciones de desarrollo:

const promise = Promise.reject(new Error('Failed'))

promise.catch(err => console.log('Handled'))

Este matiz de temporización explica por qué algunos rechazos manejados correctamente aún pueden aparecer como no manejados durante el desarrollo.

Patrones adecuados de manejo de errores con async/await

try/catch local alrededor de await

El patrón más confiable envuelve expresiones await en try/catch:

async function fetchPokemon(id) {
  try {
    const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`)
    const data = await response.json()
    return {
      name: data.name,
      type: data.types[0]?.type.name,
      type2: data.types[1]?.type.name ?? null
    }
  } catch (error) {
    console.error('Fetch failed:', error.message)
    return null
  }
}

Devolver Promises con manejadores adjuntos

Al llamar funciones async, siempre maneja la Promise devuelta:

// Opción 1: await con try/catch
await fetchPokemon(25)

// Opción 2: .catch() en la llamada
fetchPokemon(25).catch(handleError)

Encadenar .catch() apropiadamente

Coloca .catch() al final de las cadenas de Promise para capturar cualquier rechazo en la cadena:

Promise.all(urls.map(url => fetch(url).then(r => r.json())))
  .then(results => processResults(results))
  .catch(error => showErrorUI(error))

Usar el evento unhandledrejection para monitoreo

Los navegadores proporcionan el evento unhandledrejection para monitoreo global de errores, no como mecanismo principal de manejo de errores:

window.addEventListener('unhandledrejection', event => {
  // Registrar en servicio de monitoreo de errores
  errorTracker.capture(event.reason)

  // Opcionalmente prevenir el error de consola predeterminado
  event.preventDefault()
})

El evento complementario rejectionhandled se dispara cuando un rechazo previamente no manejado recibe posteriormente un manejador. Estos eventos son útiles para telemetría y para capturar errores que se escapan de tu manejo local, pero no deben reemplazar los patrones adecuados de try/catch y .catch().

Conclusión

El mensaje “Uncaught (in promise) TypeError” señala tanto un error en tiempo de ejecución como un manejo de errores faltante. Aborda ambos lados del problema: usa encadenamiento opcional (?.) y fusión de nulos (??) para prevenir TypeErrors de propiedades indefinidas, y envuelve operaciones async en try/catch o adjunta manejadores .catch() a cada cadena de Promise. Reserva el evento unhandledrejection para monitoreo, no para flujo de control. Estos patrones mantendrán tu código frontend resiliente y tu consola limpia.

Preguntas frecuentes

Un Uncaught TypeError es un error síncrono que detiene la ejecución inmediatamente. Un Uncaught (in promise) TypeError es el mismo tipo de error en tiempo de ejecución, pero ocurrió dentro de una Promise que no tenía manejador de rechazo. El navegador agrega la etiqueta 'in promise' para indicar que el rechazo quedó sin manejar en la cadena asíncrona de Promise.

Sí. Un solo .catch() al final de una cadena de Promise captura cualquier rechazo que ocurra en cualquier callback .then() precedente. El rechazo se propaga por la cadena hasta que encuentra un manejador .catch(). Si no existe ninguno, el navegador lo reporta como un rechazo de Promise no manejado.

Un bloque try/catch dentro de un callback .then() puede capturar errores síncronos lanzados dentro de ese callback. Sin embargo, no puede capturar rechazos de Promise a menos que esas Promises sean esperadas con await dentro de una función async. Para manejar rechazos en una cadena .then(), adjunta un .catch() al final de la cadena. Reserva try/catch principalmente para patrones async/await.

No. El evento unhandledrejection es una red de seguridad para capturar rechazos de Promise que se escapan de tu manejo local de errores. Es más adecuado para registro y monitoreo. Siempre maneja los errores localmente con try/catch o .catch() primero, y trata el listener de evento global como un respaldo para diagnósticos.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay