ES /docs

EfsManager::copyTo | end - {"errno":-2,"code":"ENOENT","syscall":"copyfile","path":"/tmp/workspace/6

RCA: EfsManager::copyTo ENOENT on alignment_archive.bin

Error Log#

Datadog Logs

text
EfsManager::copyTo | end - {"errno":-2,"code":"ENOENT","syscall":"copyfile","path":"/tmp/workspace/67246/results/alignment_archive.bin","dest":"/efs/753b0e42ed4cfcc7/skatmaster/results/alignment_archive.bin"}

Impact#

  • Service: cupixworks-captue-skatmaster-arm-instance
  • Team: built
  • 발생 횟수: 21
  • 최초 발생: 2026-04-06T09:24:20.529Z
  • 최근 발생: 2026-04-07T09:19:14.478Z

Root Cause Summary#

SceneMapper(skat) 프로세스가 완료된 후, copyAlignPreviewFilesToSkatMasterResult() 메서드가 alignment_archive.bin 파일을 결과 디렉토리에서 EFS로 복사하려고 시도합니다. 그러나 SceneMapper SDK가 모든 실행에서 alignment_archive.bin을 생성하는 것은 아닙니다 — 이 파일은 align preview 기능이 활성화되고 정상적으로 처리된 경우에만 생성됩니다. copyAlignPreviewFilesToSkatMasterResult() 메서드는 파일 존재 여부를 확인하지 않고 fs.copyFileSync()를 바로 호출하기 때문에, 파일이 없으면 ENOENT 에러가 발생합니다. 이 에러는 catch 블록에서 잡혀 warn 레벨로 로깅되고 false를 반환하므로 프로세스 자체는 중단되지 않지만, EfsManager::copyTo에서 logger.error()로 먼저 기록되어 에러 클러스터로 수집됩니다.

Technical Analysis#

Code Path#

  • Entry point: skatmaster-service.ts:58runSkatMaster() 호출
  • skatmaster.manager.ts:204 — SceneMapper 프로세스 실행 (this.execute(params))
  • skatmaster.manager.ts:206-208saveSkatMasterResults() 호출 (alignment JSON 결과 저장, 성공)
  • Failure point: skatmaster.manager.ts:209copyAlignPreviewFilesToSkatMasterResult() 호출

copyAlignPreviewFilesToSkatMasterResult() 메서드는 resultDirPath에서 세 파일(alignment_archive.bin, align_preview_default.png, align_preview_meta_default.json)을 EFS로 복사합니다. 파일 존재 여부 확인 없이 바로 copyTo()를 호출합니다:

typescript
// skatmaster.manager.ts:497-518
copyAlignPreviewFilesToSkatMasterResult = async (): Promise<boolean> => {
    if (this.resultDirPath == undefined) {
        logger.warn('SkatmasterManager::copyAlignPreviewFilesToSkatMasterResult | undefined results directory path');
        return false;
    }

    try {
        const fileList = await CPUtils.getFiles(this.resultDirPath);
        logger.debug('...results file list - %s', JSON.stringify(fileList));

        const binFilePath = path.join(this.resultDirPath, Constants.DefaultAlignPreviewBinFileName);
        this.efsManager.copyTo(binFilePath, path.join(this.efsManager.skatMasterResultPath, Constants.DefaultAlignPreviewBinFileName));
        // ... image, json 파일도 동일하게 복사
        return true;
    } catch (e) {
        logger.warn('...exception occurred while copy align preview files to efs. error: %s', JSON.stringify(e));
        return false;
    }
};

copyTo() 메서드는 fs.copyFileSync()를 감싸는 래퍼로, 소스 파일이 없으면 ENOENT를 throw합니다:

typescript
// efs.manager.ts:40-51
copyTo = (source: string, target: string): void => {
    try {
        logger.debug('EfsManager::copyTo | source: %s, target: %s, fileSize: %s', source, target, CPUtils.formatBytesString(CPUtils.getFileSize(source)));
        const start = new Date().getTime();
        fs.copyFileSync(source, target);
        const durationTime = new Date().getTime() - start;
        logger.debug('EfsManager::copyTo | done - duration time: %d milliseconds', durationTime);
    } catch(e) {
        logger.error('EfsManager::copyTo | end - %s', JSON.stringify(e));  // <-- 에러 로그 발생 지점
        throw e;
    }
};

비교: saveSkatMasterResults() (efs.manager.ts:90-119)와 copyProcessOutputToEFS() (efs.manager.ts:121-146)에서는 fs.existsSync()로 파일 존재 여부를 먼저 확인하고 없으면 경고 후 건너뜁니다. copyAlignPreviewFilesToSkatMasterResult()에만 이 확인이 누락되어 있습니다.

typescript
// efs.manager.ts:131-134 — copyProcessOutputToEFS의 파일 존재 확인 (올바른 패턴)
if (!fs.existsSync(sourcePath)) {
    logger.warn('EfsManager::copyProcessOutputToEFS | source does not exists %s', sourcePath);
    isAllCopied = false;
    continue;
}

Log Evidence#

Datadog에서 사용한 쿼리:

text
service:cupixworks-captue-skatmaster-arm-instance status:error "EfsManager::copyTo"

에러 로그 패턴 — 모든 21건이 동일한 패턴 (alignment_archive.binENOENT):

json
{
  "timestamp": "2026-04-07 18:19:14 KST",
  "status": "error",
  "message": "EfsManager::copyTo | end - {\"errno\":-2,\"code\":\"ENOENT\",\"syscall\":\"copyfile\",\"path\":\"/tmp/workspace/676194/results/alignment_archive.bin\",\"dest\":\"/efs/cfb6084d36428188/skatmaster/results/alignment_archive.bin\"}"
}
json
{
  "timestamp": "2026-04-07 14:14:22 KST",
  "status": "error",
  "message": "EfsManager::copyTo | end - {\"errno\":-2,\"code\":\"ENOENT\",\"syscall\":\"copyfile\",\"path\":\"/tmp/workspace/40329/results/alignment_archive.bin\",\"dest\":\"/efs/5e0ade3c1b90a87f/skatmaster/results/alignment_archive.bin\"}"
}

에러 직후 warn 로그 — copyAlignPreviewFilesToSkatMasterResult의 catch 블록과 runSkatMaster의 경고가 동일 타임스탬프에 기록됨:

text
service:cupixworks-captue-skatmaster-arm-instance "copyAlignPreviewFilesToSkatMasterResult"
json
{
  "timestamp": "2026-04-07 18:19:14 KST",
  "status": "warn",
  "message": "SkatmasterManager::copyAlignPreviewFilesToSkatMasterResult | exception occurred while copy align preview files to efs. error: {\"errno\":-2,\"code\":\"ENOENT\",\"syscall\":\"copyfile\",\"path\":\"/tmp/workspace/676194/results/alignment_archive.bin\",\"dest\":\"/efs/cfb6084d36428188/skatmaster/results/alignment_archive.bin\"}"
}
text
service:cupixworks-captue-skatmaster-arm-instance "fail to copy align preview"
json
{
  "timestamp": "2026-04-07 18:19:14 KST",
  "status": "warn",
  "message": "SkatmasterManager::runSkatMaster | fail to copy align preview files"
}

프로세스 전체는 정상 완료 — saveSkatMasterResults | write donerunSkatMaster | done 로그가 동일 세션에서 확인됨:

json
{
  "timestamp": "2026-04-07 18:36:49 KST",
  "status": "info",
  "message": "SkatmasterManager::saveSkatMasterResults | write done"
}

ChildProcessManager::setupEventHandlers | Child process exited 에러도 동시에 다수 발생하며, SceneMapper 프로세스 종료 시 일부 실행에서 align preview 파일이 생성되지 않는 것으로 보입니다.

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: skatmaster.manager.ts:507-512 (copyAlignPreviewFilesToSkatMasterResult 메서드)
  • efsManager.copyTo() 호출 전에 fs.existsSync() 체크를 추가하여, 파일이 없으면 warn 로그를 남기고 건너뛰도록 변경
  • 이미 같은 코드베이스의 copyProcessOutputToEFS() (efs.manager.ts:131-134)에 동일한 패턴이 구현되어 있으므로, 이 패턴을 따르면 됨
  • 이렇게 하면 EfsManager::copyTo에서 logger.error()가 호출되지 않아 에러 클러스터가 발생하지 않음

단기 개선 (1주 이내)#

  • alignment_archive.bin이 생성되지 않는 조건을 SceneMapper SDK 문서 또는 팀과 확인하여, 해당 파일이 선택적 출력물(optional output)인지 확인
  • 선택적 출력물이 맞다면, copyAlignPreviewFilesToSkatMasterResult() 전체를 선택적 복사 로직으로 재구성 (각 파일별 존재 확인 후 복사)

장기 개선 (재발 방지)#

  • copyTo() 메서드에 optional 파라미터를 추가하여, 소스 파일이 없을 때 에러 대신 warn을 남기고 false를 반환하는 모드를 지원하는 것을 고려
  • 또는 copyToIfExists() 유틸리티 메서드를 추가하여, 선택적 파일 복사 시 일관된 패턴을 사용

Monitoring#

  • 수정 후 에러 클러스터 재발 모니터링:
text
service:cupixworks-captue-skatmaster-arm-instance status:error "EfsManager::copyTo" "alignment_archive.bin"
  • align preview 파일 미생성 빈도 추적 (warn 레벨):
text
service:cupixworks-captue-skatmaster-arm-instance "fail to copy align preview files"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial
  • 이 에러는 프로세스 중단을 일으키지 않으며 (catch 블록에서 false 반환 후 계속 진행), align preview 파일은 후속 처리(refinement, postprocessor)에서도 선택적으로 사용됨. 수정은 fs.existsSync() 한 줄 추가 수준으로 간단함.