Comment gérer l'erreur Uncaught (in promise) TypeError
Vous déboguez votre application frontend lorsque la console affiche : Uncaught (in promise) TypeError: Cannot read property 'type' of undefined. Ce message d’erreur contient deux problèmes distincts que les développeurs confondent souvent. Comprendre les deux est essentiel pour une gestion appropriée des erreurs de Promise en JavaScript.
Cet article explique ce que cette erreur signifie réellement, pourquoi elle apparaît et comment gérer correctement les rejets de Promise non gérés dans les environnements de navigateur.
Points clés à retenir
- Le message « Uncaught (in promise) TypeError » signale deux problèmes distincts : une TypeError d’exécution et l’absence de gestionnaire de rejet de Promise.
- Les causes courantes incluent les Promises flottantes (absence de
await), les gestionnaires.catch()oubliés et l’accès à des propriétés surundefinedounull. - Encapsulez les expressions
awaitdanstry/catchou attachez.catch()à la fin de chaque chaîne de Promise pour éviter les rejets non gérés. - Utilisez le chaînage optionnel (
?.) et la coalescence nulle (??) pour vous protéger contre les TypeErrors provenant de propriétés non définies. - Réservez l’événement
unhandledrejectionà la surveillance et à la télémétrie, et non comme substitut à la gestion locale des erreurs.
Ce que signifie réellement ‘Uncaught (in promise) TypeError’
Ce message d’erreur communique deux problèmes distincts :
- TypeError : Une erreur d’exécution s’est produite, généralement lors de l’accès à une propriété sur
undefinedounull. - Uncaught (in promise) : La Promise qui a produit cette erreur n’avait aucun gestionnaire de rejet attaché.
Le navigateur distingue ces deux aspects car les rejets de Promise se comportent différemment des erreurs synchrones. Lorsqu’une TypeError synchrone se produit, l’exécution s’arrête immédiatement. Lorsque la même erreur se produit à l’intérieur d’une Promise, le rejet se propage à travers la chaîne de Promise. Si aucun .catch() ou try/catch ne la gère, le navigateur l’enregistre comme un rejet de Promise non géré.
Pour une référence plus approfondie sur le comportement des Promises et la propagation des erreurs, consultez la documentation MDN sur les Promises.
Causes profondes courantes dans le code frontend
Erreurs levées à l’intérieur de fonctions async
Toute exception à l’intérieur d’une fonction async rejette automatiquement la Promise retournée :
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
return data.profile.name // TypeError si profile est undefined
}
fetchUserData(123) // Promise flottante—aucun gestionnaire attaché
Mots-clés await manquants
Oublier await crée une Promise flottante qui s’exécute indépendamment :
async function processData() {
fetchUserData(123) // await manquant—le rejet n'est pas géré
console.log('Done')
}
Gestionnaires .catch() oubliés
Les chaînes de Promise sans bloc .catch() terminal laissent les rejets non gérés :
fetch('/api/data')
.then(res => res.json())
.then(data => data.items[0].name) // Pas de .catch()—la TypeError se propage
Attachement tardif de gestionnaire et timing des microtâches
Les navigateurs déterminent si un rejet est géré après le point de contrôle de microtâche actuel. Si un rejet n’a pas de gestionnaire à ce moment-là, le navigateur peut émettre un événement unhandledrejection et enregistrer un avertissement dans la console. Attacher un gestionnaire peu après peut toujours déclencher l’avertissement dans les versions de développement :
const promise = Promise.reject(new Error('Failed'))
promise.catch(err => console.log('Handled'))
Cette nuance de timing explique pourquoi certains rejets correctement gérés peuvent encore apparaître comme non gérés pendant le développement.
Discover how at OpenReplay.com.
Modèles appropriés de gestion d’erreurs avec async/await
try/catch local autour de await
Le modèle le plus fiable encapsule les expressions await dans 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('Échec de la récupération:', error.message)
return null
}
}
Retourner des Promises avec des gestionnaires attachés
Lors de l’appel de fonctions async, gérez toujours la Promise retournée :
// Option 1 : await avec try/catch
await fetchPokemon(25)
// Option 2 : .catch() sur l'appel
fetchPokemon(25).catch(handleError)
Chaîner .catch() de manière appropriée
Placez .catch() à la fin des chaînes de Promise pour capturer tout rejet dans la chaîne :
Promise.all(urls.map(url => fetch(url).then(r => r.json())))
.then(results => processResults(results))
.catch(error => showErrorUI(error))
Utiliser l’événement unhandledrejection pour la surveillance
Les navigateurs fournissent l’événement unhandledrejection pour la surveillance globale des erreurs, et non comme mécanisme principal de gestion des erreurs :
window.addEventListener('unhandledrejection', event => {
// Enregistrer dans le service de surveillance des erreurs
errorTracker.capture(event.reason)
// Optionnellement empêcher l'erreur de console par défaut
event.preventDefault()
})
L’événement complémentaire rejectionhandled se déclenche lorsqu’un rejet précédemment non géré reçoit ultérieurement un gestionnaire. Ces événements sont utiles pour la télémétrie et pour capturer les erreurs qui échappent à votre gestion locale, mais ils ne doivent pas remplacer les modèles appropriés try/catch et .catch().
Conclusion
Le message « Uncaught (in promise) TypeError » signale à la fois une erreur d’exécution et une gestion d’erreur manquante. Traitez les deux aspects du problème : utilisez le chaînage optionnel (?.) et la coalescence nulle (??) pour éviter les TypeErrors provenant de propriétés non définies, et encapsulez les opérations async dans try/catch ou attachez des gestionnaires .catch() à chaque chaîne de Promise. Réservez l’événement unhandledrejection à la surveillance, et non au flux de contrôle. Ces modèles rendront votre code frontend résilient et votre console propre.
FAQ
Une Uncaught TypeError est une erreur synchrone qui arrête l'exécution immédiatement. Une Uncaught (in promise) TypeError est le même type d'erreur d'exécution, mais elle s'est produite à l'intérieur d'une Promise qui n'avait pas de gestionnaire de rejet. Le navigateur ajoute le label 'in promise' pour indiquer que le rejet n'a pas été géré dans la chaîne de Promise asynchrone.
Oui. Un seul .catch() à la fin d'une chaîne de Promise capture tout rejet qui se produit dans n'importe quel callback .then() précédent. Le rejet se propage dans la chaîne jusqu'à ce qu'il trouve un gestionnaire .catch(). S'il n'en existe aucun, le navigateur le signale comme un rejet de Promise non géré.
Un bloc try/catch à l'intérieur d'un callback .then() peut capturer les erreurs synchrones levées dans ce callback. Cependant, il ne peut pas capturer les rejets de Promise à moins que ces Promises ne soient attendues avec await à l'intérieur d'une fonction async. Pour gérer les rejets dans une chaîne .then(), attachez un .catch() à la fin de la chaîne. Réservez try/catch principalement aux modèles async/await.
Non. L'événement unhandledrejection est un filet de sécurité pour capturer les rejets de Promise qui échappent à votre gestion locale des erreurs. Il est mieux adapté à la journalisation et à la surveillance. Gérez toujours les erreurs localement avec try/catch ou .catch() en premier lieu, et traitez l'écouteur d'événement global comme un mécanisme de secours pour les diagnostics.
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.