Error: Import failed는 오류를 만드는 줄을 가리킵니다. 그 줄은 앱이 실패를 보고하는 방식입니다. 실제 실패를 찾으려면 첨부된 cause에서 파서로 이동하고 전달된 입력을 확인하세요.

JavaScript 스택 추적 예제 다운로드 후 압축을 풀고 read-javascript-stack-trace로 이동하세요. 로컬 파일과 Node 내장 모듈만 사용하여 설치나 네트워크 요청이 없습니다. 튜토리얼용 예제의 가져오기는 레코드를 읽고 파싱할 뿐 저장하지 않습니다. 아래 명령은 Node.js 22.22.1에서 실행했으며 Node.js 22 이상을 사용하세요.

소스 파일 세 개가 조사할 경로를 만듭니다.

파일 역할 확인할 근거
run.mjs 입력을 선택하고 가져오기 대기 입력 파일 이름, 결과, 종료 상태
importer.mjs 파일 읽기, 파서 호출, 실패 감싸기 try 블록과 잡힌 cause
parser.mjs 텍스트를 JSON.parse에 전달 파서 호출과 전달된 텍스트

실패 입력을 실행하고 두 스택 보관

node run.mjs with-cause

이 명령은 종료 상태 1로 실패하는 것이 정상입니다. 아래는 stdout과 stderr를 합친 출력이며 절대 디렉터리 접두사만 file:///lab/로 바꿨습니다. 파일 이름, 줄, 열과 오류 문구는 기록된 실행의 값입니다.

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는 error.stack과 error.cause.stack을 별도로 출력합니다. 첫 스택은 감싼 오류, 두 번째는 잡힌 SyntaxError의 스택입니다. 오류 보고를 복사할 때도 구분하세요. 외부 메시지만 저장하는 로거는 유용한 근거 대부분을 잃습니다.

importer.mjs:10:19의 마지막 숫자는 소스의 줄과 열입니다. 파서 메시지의 ‘line 2 column 28’은 JSON 입력 위치입니다. 서로 다른 좌표이므로 추적을 만든 소스 버전을 사용해 각 위치가 설명하는 파일이나 값과 연결하세요.

생성 줄을 열고 catch 분기 읽기

다운로드의 전체 importer.mjs입니다.

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

10행에서 래퍼 오류를 만들고 11행에서 기존 객체를 던집니다. Node가 new Error() 위치를 기록하므로 외부 스택은 10행에서 시작합니다. 나중에 같은 객체를 던져도 기록된 위치는 이동하지 않습니다. Node.js 22 오류 문서가 이를 설명합니다.

try 블록도 중요합니다. readFile과 parseRecords 모두 실패할 수 있고 같은 외부 메시지를 받습니다. ‘Import failed’만으로는 실패한 분기를 알 수 없습니다. 예를 들어 파일 누락 테스트는 SyntaxError 대신 ENOENT 원인을 받습니다.

cause에서 파서와 실제 입력까지 따라가기

원인 스택은 먼저 JSON.parse를 가리킵니다. 확인 가능한 코드의 첫 프레임인 parser.mjs:2:15로 이동하세요.

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

text를 readFile(input, 'utf8')까지 거슬러 따라가세요. run.mjs가 선택한 records.bad.json의 전체 내용은 다음과 같습니다.

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

2 뒤 쉼표 때문에 파서는 다음 속성 이름을 기다리지만 보고된 위치에서 }를 만납니다. 이제 입력, 구체적 연산과 재현 가능한 실패가 있습니다. 래퍼 생성 줄은 조사 시작점이며 메시지를 바꿔도 JSON은 그대로입니다.

new Error(message, { cause })는 잡힌 값을 새 오류에서 접근할 수 있게 보존합니다. Node 16.9.0에 추가되었고 여기서 사용한 22.22.1 문서에도 있습니다. 값은 무엇이든 될 수 있으므로 일반 오류 처리에서는 .stack을 읽기 전에 유형을 확인하세요. 이 예제는 instanceof Error를 검사합니다. Node의 error.cause 문서를 참고하세요.

cause를 제거하고 사라지는 정보 확인

node run.mjs without-cause

이 명령도 상태 1로 종료합니다. 같은 입력이 실패하지만 출력은 이제 다음으로 끝납니다.

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

Cause: unavailable

이 모드는 new Error에 cause 옵션을 전달하지 않아 파서 오류가 보존되지 않습니다. 스택만으로 잃어버린 입력 위치를 복구하거나 파일 누락과 구분할 수 없습니다. 원래 오류를 보존한 로컬 재현 같은 추가 관찰이 필요합니다.

cause 보존은 조사를 돕지만 입력을 고치지는 않습니다. 첫 명령처럼 with-cause도 여전히 실패합니다.

비동기 프레임을 단서로 삼고 코드로 빈틈 채우기

두 스택 모두 await하는 호출자인 at async .../run.mjs:14:19를 포함합니다. 파싱 실패 시점에는 readFile이 이미 완료되어, 결과를 파서에 제공했어도 그 작업이 스택에는 없습니다.

V8 비동기 스택 문서는 await, Promise.all() 및 Promise.any() 등 일부 Promise 연산 주변의 재구성을 설명합니다. 이 프레임들은 선택된 비동기 관계만 보여주며 콜백, 타이머나 I/O의 전체 기록은 아닙니다. 프레임이 없다고 실행되지 않았다는 뜻은 아닙니다.

SyntaxError 문구와 프레임은 Node/V8 버전에 따라 달라질 수 있습니다. 표시된 위치로 해당 소스를 열되 이 출력을 보편적인 스택 형식으로 여기지 마세요. 예제 테스트는 전체 스택 문자열 대신 오류 속성과 앱 프레임을 확인합니다.

입력을 기준으로 재시도 가설 검증

‘일시적인 네트워크 문제이니 재시도하면 해결된다’는 주장을 검토해 봅시다. 이는 검증할 학습용 가설이며 실제 사고나 AI 시스템의 인용이 아닙니다.

스택은 JSON.parse의 SyntaxError를 보여줍니다. 소스에서는 파서가 로컬 파일 바이트를 받습니다. 이 예제에는 네트워크 작업이 없으며 같은 잘못된 바이트로 세 번 시도해도 모두 실패합니다. 이 설명을 뒷받침할 근거가 없습니다.

후행 쉼표만 제거한 수정 fixture를 실행하세요.

node run.mjs fixed

종료 상태 0과 다음 출력을 얻습니다.

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

이어서 회귀 검사를 실행하세요.

node --test importer.test.mjs

테스트 9개가 모두 통과합니다. 파서 원인 보존, 원인을 잃은 래퍼, 동일 입력의 반복 실패, 수정 입력 성공, 파일 누락 원인, 생성과 나중 throw의 차이 및 CLI 모드 세 개를 다룹니다. 실패하는 시연 명령 두 개는 예상된 실패이며 회귀 테스트 실패가 아닙니다.

이로써 해당 로컬 바이트의 실패가 설명됩니다. 실제 가져오기 기능에서는 잘못된 텍스트를 만든 곳까지 따라간 뒤 원인을 판단하세요. 예제는 JSON 구문과 결과를 확인할 뿐 레코드 스키마를 검증하거나 가져온 데이터를 저장하지 않습니다.

Yenpo에서 주변 코드를 읽고 터미널에서 검증

압축을 푼 폴더를 Yenpo에서 열고 코드 읽기 가이드에 따라 importer.mjs, parser.mjs와 선택된 입력을 찾으세요. 보고된 줄에서 시작해 catch 분기 전체와 input을 전달한 호출자를 읽으세요.

모델을 설정했다면 Yenpo AI Reader로 경로 조사를 도울 수 있습니다.

Import failed 오류를 생성 줄에서 cause를 거쳐 JSON.parse에 전달된 입력까지 추적해 주세요. 파일과 줄을 인용하고 스택에서 보이는 내용, 소스로 확인된 내용 및 터미널 실행이 필요한 내용을 구분해 주세요. 이 예제에서 재시도가 바이트를 바꿀 수 있는지 확인해 주세요.

터미널에서 명령을 실행하고 출력과 설명을 비교하세요.