Error: Import failed aponta para uma linha que cria um erro. Abri-la mostra como a aplicação relata uma falha. Para encontrar o que realmente falhou, siga o cause anexado até o parser e examine a entrada recebida.

Baixe o laboratório completo de stack trace JavaScript, descompacte e entre em read-javascript-stack-trace. Ele usa arquivos locais e módulos nativos do Node, sem instalação nem rede. O exemplo foi construído para este tutorial. A importação lê e analisa registros sem salvá-los. Os comandos foram executados com Node.js 22.22.1; use Node.js 22 ou posterior.

Os três arquivos formam um pequeno caminho de investigação:

Arquivo Trabalho realizado O que examinar
run.mjs Escolhe uma entrada e aguarda a importação Nome da entrada, resultado e status de saída
importer.mjs Lê o arquivo, chama o parser e encapsula falhas Bloco try e cause capturado
parser.mjs Passa texto a JSON.parse Chamada do parser e texto fornecido

Execute a entrada que falha e preserve as duas pilhas

node run.mjs with-cause

Este comando deve falhar com status 1. A saída abaixo combina stdout e stderr; apenas o prefixo absoluto do diretório foi substituído por file:///lab/. Nomes, linhas, colunas e mensagem vêm da execução registrada.

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 imprime error.stack e error.cause.stack separadamente. A primeira pilha pertence ao erro externo; a segunda, ao SyntaxError capturado. Preserve a distinção ao copiar um relato. Um logger que salva apenas a mensagem externa descarta quase toda a informação útil aqui.

Em importer.mjs:10:19, os dois números finais indicam linha e coluna do código. Na mensagem do parser, “line 2 column 28” refere-se ao JSON de entrada. São sistemas de coordenadas diferentes. Associe cada posição ao arquivo ou valor descrito, usando a mesma versão que produziu o trace.

Abra a linha de construção e leia o bloco catch

Este é o importer.mjs completo do download:

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;
  }
}

A linha 10 cria o erro externo. A 11 lança esse objeto existente. A pilha externa começa na linha 10 porque o Node registra a posição de new Error(). Lançar o mesmo objeto depois não muda essa posição capturada. A documentação de erros do Node.js 22 descreve o comportamento.

O bloco try também importa: readFile e parseRecords podem falhar e recebem a mesma mensagem externa. “Import failed” sozinho não identifica o caminho que falhou. Por exemplo, o teste de arquivo ausente tem causa ENOENT, não SyntaxError.

Siga a causa até o parser e sua entrada real

A pilha da causa nomeia primeiro JSON.parse. Continue até o primeiro frame do código que pode examinar: parser.mjs:2:15.

export function parseRecords(text) {
  return JSON.parse(text);
}

Depois siga text até readFile(input, 'utf8'). run.mjs selecionou records.bad.json, cujo conteúdo completo é:

[
  {"id": "tea", "count": 2,}
]

A vírgula após 2 faz o parser esperar outro nome de propriedade. Em vez disso, encontra } na posição indicada. Agora há uma entrada, uma operação específica e uma falha reproduzível. A linha que constrói o erro externo iniciou a investigação; mudar sua mensagem deixaria esse JSON intacto.

new Error(message, { cause }) mantém o valor capturado acessível no novo erro. O Node adicionou a opção em 16.9.0; ela está documentada para 22.22.1, usada aqui. O valor pode ser de qualquer tipo, então um tratador genérico deve conferir o tipo antes de ler .stack. O impressor do laboratório verifica instanceof Error. Veja a referência de error.cause.

Remova a causa e veja o que desaparece

node run.mjs without-cause

Também sai com status 1. A mesma entrada falha, mas a saída agora termina com:

Error: Import failed
    at importRecords (file:///lab/importer.mjs:10:19)
    at async file:///lab/run.mjs:14:19

Cause: unavailable

Este modo não passa a opção cause a new Error. O erro externo deixa de carregar o erro do parser. Sua pilha não recupera a posição descartada na entrada nem distingue essa falha do arquivo ausente. Seria necessária outra observação, como uma reprodução local preservando o erro original.

Manter cause melhora a investigação. Não corrige a entrada: with-cause ainda falha, como mostrou o primeiro comando.

Use frames assíncronos como pistas e complete pelo código

As duas pilhas registradas incluem at async .../run.mjs:14:19, o chamador que aguarda. Quando a análise falha, readFile já resolveu; seu trabalho não aparece no trace, embora seu resultado tenha alimentado o parser.

A documentação de pilhas assíncronas do V8 descreve a reconstrução em torno de await e operações selecionadas, incluindo Promise.all() e Promise.any(). Esses frames cobrem relações assíncronas específicas, não um histórico completo de callbacks, temporizadores ou I/O. A ausência de um frame não prova que o trabalho não executou.

O texto do SyntaxError e os frames disponíveis variam entre versões Node/V8. Use as posições para abrir o código correspondente; não trate esta transcrição como formato universal. Os testes conferem propriedades úteis do erro e frames da aplicação sem comparar toda a string da pilha.

Teste a hipótese de nova tentativa contra a entrada

Considere: “É um problema temporário de rede; tentar novamente deve resolver.” É uma hipótese didática, não uma citação de IA ou incidente real.

O trace mostra um SyntaxError em JSON.parse. O código mostra que o parser recebeu bytes de um arquivo local. Não há operação de rede no laboratório, e três tentativas com os mesmos bytes inválidos rejeitam. A explicação proposta não tem sustentação aqui.

Agora execute a fixture corrigida, que remove apenas a vírgula final:

node run.mjs fixed

Ela sai com status 0 e imprime:

Input: records.fixed.json
Parsed records: [{"id":"tea","count":2}]

Depois execute as verificações de regressão:

node --test importer.test.mjs

Os nove testes passam. Cobrem causa do parser preservada, encapsulamento com perda, falha repetida com entrada igual, sucesso da entrada corrigida, causa de arquivo ausente, construção versus lançamento posterior e os três modos CLI. Os dois comandos demonstrativos que falham são falhas esperadas, não testes de regressão quebrados.

Isso estabelece a explicação para esses bytes locais. Em um importador real, siga até o produtor do texto inválido antes de decidir por que estava inválido. O laboratório verifica sintaxe JSON e resultado do exemplo; não valida schema de registros nem persiste a importação.

Leia o contexto no Yenpo e verifique no terminal

Abra a pasta extraída no Yenpo e use o guia de leitura para localizar importer.mjs, parser.mjs e a entrada escolhida. Comece nas linhas relatadas, depois leia todo o catch e o chamador que fornece input.

Com um modelo configurado, o AI Reader do Yenpo ajuda a examinar o caminho. Um pedido específico seria:

Rastreie este erro Import failed da linha de construção através de cause até a entrada de JSON.parse. Cite arquivos e linhas. Distinga o que a pilha mostra, o que o código estabelece e o que ainda precisa ser executado no terminal. Confira se tentar novamente pode mudar os bytes deste exemplo.

Execute os comandos no terminal e compare a saída com a explicação.