Error: Import failed pointe vers une ligne qui crée une erreur. L’ouvrir montre comment l’application signale l’échec. Pour trouver ce qui a réellement échoué, suivez le cause attaché jusqu’au parseur, puis inspectez l’entrée reçue.
Téléchargez l’atelier complet de traces JavaScript, décompressez-le et entrez dans read-javascript-stack-trace. Il utilise des fichiers locaux et les modules intégrés de Node, sans installation ni requête réseau. L’exemple est construit pour ce tutoriel. L’import lit et analyse des enregistrements sans les enregistrer. Toutes les commandes ci-dessous ont été exécutées avec Node.js 22.22.1 ; utilisez Node.js 22 ou ultérieur.
Les trois fichiers source donnent un petit chemin à analyser :
| Fichier | Travail effectué | Éléments à inspecter |
|---|---|---|
run.mjs |
Choisit une entrée et attend l’import | Nom du fichier d’entrée, résultat, statut de sortie |
importer.mjs |
Lit le fichier, appelle le parseur, enveloppe les erreurs | Bloc try et cause capturé |
parser.mjs |
Transmet le texte à JSON.parse |
Appel du parseur et texte fourni |
Exécuter l’entrée invalide et conserver les deux piles
node run.mjs with-cause
Cette commande doit échouer avec le statut 1. La sortie ci-dessous combine stdout et stderr ; seul le préfixe de répertoire absolu a été remplacé par file:///lab/. Noms de fichiers, lignes, colonnes et texte d’erreur proviennent de l’exécution consignée.
Input: records.bad.json
Error: Import failed
at importRecords (file:///lab/importer.mjs:10:19)
at async file:///lab/run.mjs:14:19
Caused by:
SyntaxError: Expected double-quoted property name in JSON at position 29 (line 2 column 28)
at JSON.parse (<anonymous>)
at parseRecords (file:///lab/parser.mjs:2:15)
at importRecords (file:///lab/importer.mjs:7:12)
at async file:///lab/run.mjs:14:19
run.mjs affiche séparément error.stack et error.cause.stack. La première pile appartient à l’enveloppe, la seconde au SyntaxError capturé. Gardez cette distinction dans un rapport. Un journal qui ne conserve que le message externe perd ici l’essentiel des indices utiles.
Dans importer.mjs:10:19, les deux derniers nombres désignent une ligne et une colonne du source. Dans le message du parseur, « line 2 column 28 » concerne l’entrée JSON. Ce sont deux systèmes de coordonnées. Associez chaque emplacement au bon fichier ou à la bonne valeur, avec la version ayant produit la trace.
Ouvrir la ligne de création, puis lire la branche catch
Voici le fichier importer.mjs complet du téléchargement :
import { readFile } from 'node:fs/promises';
import { parseRecords } from './parser.mjs';
export async function importRecords(input, { preserveCause = true } = {}) {
try {
const text = await readFile(input, 'utf8');
return parseRecords(text);
} catch (cause) {
const options = preserveCause ? { cause } : undefined;
const error = new Error('Import failed', options);
throw error;
}
}
La ligne 10 crée l’enveloppe. La ligne 11 lance cet objet existant. La pile externe commence à la ligne 10, car Node enregistre l’emplacement de new Error(). Relancer le même objet plus tard ne déplace pas cette position capturée. La documentation des erreurs de Node.js 22 décrit ce comportement.
Le bloc try compte aussi : readFile et parseRecords peuvent échouer, avec le même message externe. « Import failed » seul ne distingue pas la branche. Par exemple, le test du fichier absent reçoit une cause ENOENT plutôt qu’un SyntaxError.
Suivre la cause jusqu’au parseur et à son entrée réelle
La pile de cause commence par JSON.parse. Continuez jusqu’à la première frame du code inspectable : parser.mjs:2:15.
export function parseRecords(text) {
return JSON.parse(text);
}
Remontez ensuite text jusqu’à readFile(input, 'utf8'). run.mjs a choisi records.bad.json, dont voici le contenu complet :
[
{"id": "tea", "count": 2,}
]
La virgule après 2 fait attendre un autre nom de propriété au parseur. Il rencontre } à l’emplacement signalé. Vous avez maintenant une entrée, une opération précise et un échec reproductible. La création de l’enveloppe était le point d’entrée de l’analyse ; changer son message ne modifierait pas ce JSON.
new Error(message, { cause }) garde la valeur capturée accessible sur la nouvelle erreur. Node a ajouté cette option en 16.9.0 ; elle est documentée pour 22.22.1, utilisé ici. La valeur pouvant être quelconque, le code générique doit vérifier son type avant de lire .stack. L’afficheur de l’atelier vérifie instanceof Error. Voir la référence Node de error.cause.
Retirer la cause et voir les informations perdues
node run.mjs without-cause
Cette commande sort aussi avec le statut 1. La même entrée échoue toujours, mais la sortie se termine maintenant ainsi :
Error: Import failed
at importRecords (file:///lab/importer.mjs:10:19)
at async file:///lab/run.mjs:14:19
Cause: unavailable
Ce mode ne fournit pas d’option cause à new Error. L’enveloppe ne porte plus l’erreur du parseur. Sa pile ne permet ni de retrouver l’emplacement d’entrée supprimé ni de distinguer ce cas du fichier absent. Il faudrait une autre observation, par exemple une reproduction locale conservant l’erreur originale.
Conserver cause améliore l’analyse, sans réparer l’entrée : with-cause échoue toujours, comme l’a montré la première commande.
Utiliser les frames asynchrones comme indices et combler les lacunes par le code
Les deux piles contiennent at async .../run.mjs:14:19, l’appelant en attente. Quand l’analyse échoue, readFile est déjà résolu ; son travail est absent de la trace, bien que son résultat alimente le parseur.
La documentation V8 des piles asynchrones décrit la reconstruction autour de await et de certaines opérations Promise, dont Promise.all() et Promise.any(). Ces frames couvrent certaines relations asynchrones, pas l’historique complet des rappels, minuteurs ou E/S. Une frame absente ne prouve pas que le travail n’a jamais eu lieu.
Le texte de SyntaxError et les frames disponibles varient selon Node/V8. Utilisez les emplacements affichés pour ouvrir le source correspondant, sans considérer cette transcription comme un format universel. Les tests vérifient des propriétés utiles et des frames applicatives sans comparer toute la chaîne de pile.
Tester l’hypothèse d’une nouvelle tentative sur l’entrée
Considérez l’affirmation « C’est un problème réseau temporaire ; réessayer devrait le résoudre. » C’est une hypothèse pédagogique à tester, pas une citation d’IA ni un incident réel.
La trace montre un SyntaxError dans JSON.parse. Le source indique que les octets viennent d’un fichier local. Aucune opération réseau n’existe dans l’atelier et trois tentatives avec les mêmes octets invalides sont toutes rejetées. L’explication proposée n’est pas étayée ici.
Exécutez maintenant la fixture corrigée, où seule la virgule finale a été retirée :
node run.mjs fixed
Elle sort avec le statut 0 et affiche :
Input: records.fixed.json
Parsed records: [{"id":"tea","count":2}]
Lancez ensuite les vérifications de régression :
node --test importer.test.mjs
Les neuf tests passent. Ils couvrent la cause du parseur conservée, l’enveloppe avec perte, l’échec répété à entrée inchangée, l’entrée corrigée, le fichier absent, la création face au lancement ultérieur et les trois modes CLI. Les deux démonstrations en échec sont des échecs attendus, pas des régressions échouées.
Cela établit l’explication pour ces octets locaux. Dans un vrai importateur, remontez au producteur du texte invalide avant d’expliquer pourquoi il l’était. L’atelier vérifie la syntaxe JSON et le résultat d’exemple, pas un schéma de données ni la persistance d’un import.
Lire le contexte dans Yenpo, vérifier dans le terminal
Ouvrez le dossier extrait dans Yenpo et utilisez le guide de lecture pour trouver importer.mjs, parser.mjs et l’entrée choisie. Partez des lignes signalées, puis lisez toute la branche catch et l’appelant qui fournit input.
Avec un modèle configuré, l’AI Reader de Yenpo peut aider à inspecter ce chemin. Une demande ciblée serait :
Suis cette erreur Import failed depuis sa création, via cause, jusqu’à l’entrée de JSON.parse. Cite fichiers et lignes. Distingue ce que montre la pile, ce qu’établit le source et ce qui exige encore une exécution en terminal. Vérifie si réessayer peut changer les octets de cet exemple.
Exécutez les commandes dans le terminal et comparez la sortie à l’explication.