ES /docs

ChildProcessManager stderr-as-error 매핑 — 경고를 에러로 분류

RCA: ChildProcessManager child process stderr — BIMSVF2Manifest.AEC Forge 404

Overview#

What Happened#

2026-06-24 13:44 KST 무렵 cupixworks-any-bimrevision-agent 서비스에서 자식 프로세스 stderr 한 줄당 한 건씩 error 레벨 로그가 다수 발생했다. 메시지 본문은 forge-agents가 Autodesk Forge의 AECModelData.json 리소스를 404(API_NOT_FOUND)로 응답받으면서 출력한 ForgeUtilsError 스택 트레이스 한 줄씩이다. 같은 클러스터 그룹(svc:cupixworks-any-bimrevision-agent::unknown)으로 20개 클러스터가 동시에 묶였고, 약 3분 뒤 자동 resolved 되었다.

Quick Facts#

Field Value
exception.class ForgeUtilsError
exception.message Resource not found: file/urn%3Aadsk.fluent%3Afs.file%3Aautodesk-360-translation-storage-prod%2Fw3cEko2vSNuSmggy9xLMHQ%2F21%2Foutput%2F0%2FAECModelData.json
top_frame node_modules/@cupixapps/forge-utils/convert-utils/svf2/svf2-manifest.js:90
runtime Node.js child process (fork) under cupixworks-any-bimrevision-agent
env production, region us-west-2, tenant cupix

Affected Teams#

Team / Domain Error Count Impact
gad / bim-revision-agent 3 로그 노이즈만 발생. BIM compare 결과는 정상적으로 후속 단계(uploadDataFiles)로 진행됨. 사용자 영향 없음.

Timeline#

  1. 2026-06-24 13:44:08 KSTBimRevisionService::runBimCompare가 자식 프로세스에 BIM 비교 요청을 위임. 자식 프로세스 내부의 forge-agents가 Autodesk Forge AECModelData.json URL에 GET 요청.
  2. 2026-06-24 13:44:10 KST — Forge가 404 응답. forge-agents 내부에서 catch되어 ForgeUtilsError 스택 트레이스가 자식 프로세스 stderr로 출력됨.
  3. 2026-06-24 13:44:10 KSTChildProcessManagerstderr.on('data') 핸들러가 stderr 청크 한 줄당 logger.error 호출. 클러스터 fingerprint가 stack frame 행마다 다르게 만들어져 20개의 svc:*::unknown 클러스터가 동시 발생.
  4. 2026-06-24 13:44:10 KSTBimRevisionService::runBimCompare | end 정상 로깅, BimRevisionService::uploadDataFiles | begin 으로 진행. forge-agents가 404를 무시 가능한(handle-internally) 케이스로 분류한 결과.
  5. 2026-06-24 13:47:03 KST — 같은 자식 프로세스에서 AWS SDK v2 deprecation NOTE가 stderr로 추가 출력. 동일 메커니즘으로 error 로그 발생. 그 후 클러스터 인시던트 auto-resolved.

Error Log#

Datadog Logs

text
ChildProcessManager::setupEventHandlers | Child process stderr:     at async BIMSVF2Manifest.AEC (/tmp/agent/dist/node_modules/.pnpm/@cupixapps+forge-utils@10.98.0_axios@1.16.1_commander@3.0.2_fflate@0.8.3_rxjs@7.8.2_three@0.170.0_ws@8.21.0/node_modules/@cupixapps/forge-utils/convert-utils/svf2/svf2-manifest.js:90:25)

전체 stderr 묶음(원문, Datadog에서 조립):

text
ForgeUtilsError: Resource not found: file/urn%3Aadsk.fluent%3Afs.file%3Aautodesk-360-translation-storage-prod%2Fw3cEko2vSNuSmggy9xLMHQ%2F21%2Foutput%2F0%2FAECModelData.json?acmsession=dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLnczY0VrbzJ2U051U21nZ3k5eExNSFE_dmVyc2lvbj0yMQ
    at ForgeClient.handleApiError (.../forge-utils/server-utils/forge-client.js:90:31)
    at ModelDataClient.getBuffer (.../forge-utils/server-utils/forge-client.js:140:25)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async ModelDataClient.getData (.../forge-utils/server-utils/model-data.js:144:24)
    at async Asset.get (.../forge-utils/convert-utils/core-utils/block.js:18:32)
    at async BIMSVF2Manifest.AEC (.../forge-utils/convert-utils/svf2/svf2-manifest.js:90:25)
    at async GridAlignment.computeGridCorrection (.../forge-agents/extractor/compare-extractor/grid-alignment.js:25:27)
    at async Object.calculateOrigin (.../forge-agents/extractor/compare-extractor/compare-utils.js:76:36)
    at async CompareExtractor.compare (.../forge-agents/extractor/compare-extractor/compare-extractor.js:62:43)
    at async extractComparison (.../forge-agents/app.js:133:20) {
  errorCode: 'API_NOT_FOUND'
}

Impact#

  • Service: cupixworks-any-bimrevision-agent
  • Team: gad
  • 발생 횟수: 3
  • 최초 발생: 2026-06-24 13:44 KST
  • 최근 발생: 2026-06-24 13:44 KST

이 클러스터(08a54588-19a5-47be-b24c-cacd7938758b)는 BIMSVF2Manifest.AEC stack frame 한 줄을 fingerprint로 가진다. 같은 incident에 같은 stderr 페이로드를 frame 단위로 묶은 19개 동반 클러스터(1ed7609e-..., 9597edbe-... 등)가 함께 묶여 있다. BIM revision 비교 자체는 정상 완료되어 후속 단계로 진행되었으므로 사용자 워크플로 차단은 발생하지 않았다.

Root Cause Summary#

ChildProcessManager가 자식 프로세스의 stderr 청크를 모두 logger.error로 출력하는 구조 때문에, 자식 안에서 내부적으로 catch되어 무시 가능한 Forge 404 스택 트레이스가 error-sweeper에서 production error로 분류되었다. 실제 BIM 비교 결과는 forge-agents가 Result.Error / Result.ErrorMessage 필드로 부모 프로세스에 IPC 전달하고, BimRevisionService::runBimCompareForgeErrorMapper.isIgnorableError 분기가 처리한다. 즉, 사용자 영향은 없는 로그 노이즈이고, root cause는 stderr → error 로깅 매핑(설계상 모든 stderr를 error로 간주)에 있다.

Technical Analysis#

Code Path#

Entry point: BimRevisionService::runBimCompare invokes the child process.

packages/cupix-tesla-bim-revision-agent/src/bim-revision-service.ts:647-672typescript
const bimCompareResults = await this.bimCompareManager.execute(params);
const newBimOrigin = bimCompareResults?.Origin;
const report = bimCompareResults?.Compare;
// ...
const forgeErrorCode = bimCompareResults?.Error;
const forgeErrorMessage = bimCompareResults?.ErrorMessage;

if (forgeErrorCode) logger.warn('CPBimRevision::validateBimComparerResult | forgeErrorCode: %s', forgeErrorCode?.toString());
if (forgeErrorMessage) logger.warn('CPBimRevision::validateBimComparerResult | forgeErrorMessage: %s', forgeErrorMessage?.toString());

if (forgeErrorCode) {
    if (ForgeErrorMapper.isIgnorableError(forgeErrorCode)) {
        logger.warn('BimRevisionService::runBimCompare | Ignorable Forge error detected - forgeErrorCode: %s, message: %s', forgeErrorCode, forgeErrorMessage);
    } else {
        // map and throw
    }
}

자식 프로세스(BimCompareProcess)는 forge-agentsextract를 호출한다. 내부에서 발생한 Forge 404는 forge-agents가 잡아 Result.Error/Result.ErrorMessage로 직렬화하여 IPC로 돌려준다. 그 과정에서 stack trace가 자식의 stderr에 출력되는 것이 본 이슈의 원인이다.

packages/cupix-tesla-bim-revision-agent/src/process/bim-compare.process.ts:67-104typescript
private async execute(params: BimComparerParams): Promise<ForgeAgent.Result> {
    try {
        // ...
        const result: ForgeAgent.Result = await ForgeAgent.extract(params.apiConfig, params.urn, {
            region: params.region,
            extractRoom: false,
            extractMeta: false,
            extractCompare: {
                urn: params.previousUrn,
                region: params.previousRegion,
                query: params.query
            }
        });
        // ... logs result.hasError / result.ErrorMessage
        return result;
    } catch (error) {
        // ...
    }
}

Failure point (로그 분류 기준): ChildProcessManager stderr 핸들러가 forge-agents의 내부 로깅을 무조건 logger.error로 흘려보낸다.

packages/base/src/manager/child-process.manager.ts:138-141typescript
this.process.stderr?.on('data', (data: Buffer) => {
    const text = data.toString();
    logger.error(`ChildProcessManager::setupEventHandlers | Child process stderr: ${text}`);
});

기대 동작: stderr 출력은 일반적으로 fatal한 것이 아니므로 warn 또는 info로 격하하거나, "process did not exit with non-zero code" 인 경우 다운그레이드한다.

실제 동작: stderr의 모든 청크가 line 단위로 잘려 각각 별개의 error 로그가 되고, error-sweeper의 fingerprint 분류기는 stack frame 라인마다 fingerprint를 다르게 계산하므로 단일 사건이 20개 클러스터로 폭증한다.

같은 메커니즘으로 노이즈를 만드는 다른 사례가 같은 분석 윈도우에 관측된다:

text
ChildProcessManager::setupEventHandlers | Child process stderr: (node:45) NOTE: The AWS SDK for JavaScript (v2) will enter maintenance mode
ChildProcessManager::setupEventHandlers | Child process stderr: on September 8, 2024 and reach end-of-support on September 8, 2025.
ChildProcessManager::setupEventHandlers | Child process stderr: Please migrate your code to use AWS SDK for JavaScript (v3).

위는 AWS SDK v2 deprecation notice인데, deprecation NOTE는 본질적으로 error가 아니다. 그럼에도 동일하게 logger.error로 기록된다.

Log Evidence#

Datadog query (재현용):

text
service:cupixworks-any-bimrevision-agent status:error "ChildProcessManager::setupEventHandlers"

원문 stderr 흐름 (2026-06-24 13:44:10 KST, Datadog):

text
13:44:10  info   BimRevisionService::runBimCompare | report.modified.length: 0, report.removed.length: 0, report.exist.length: 0
13:44:10  info   BimRevisionService::runBimCompare | report.sourceGlobalOffset=[391.60...], targetGlobalOffset=[391.60...]
13:44:10  info   BimRevisionService::runBimCompare | view comparison: prevViewId=5, curViewId=5, curViewName=Default
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr: ForgeUtilsError: Resource not found: file/urn%3Aadsk.fluent%3Afs.file%3Aautodesk-360-translation-storage-prod%2F.../AECModelData.json?...
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr:     at ForgeClient.handleApiError (.../forge-client.js:90:31)
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr:     at ModelDataClient.getBuffer (.../forge-client.js:140:25)
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr:     at async BIMSVF2Manifest.AEC (.../svf2-manifest.js:90:25)
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr:     at async GridAlignment.computeGridCorrection (.../grid-alignment.js:25:27)
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr:     at async extractComparison (.../forge-agents/app.js:133:20) {
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr:   errorCode: 'API_NOT_FOUND'
13:44:10  error  ChildProcessManager::setupEventHandlers | Child process stderr: }
13:44:10  info   CPBimRevision::validateBimComparerResult | newBimOrigin: 141.31..., currentOrigin: 141.31...
13:44:10  info   CPBimRevision::validateBimComparerResult | modified: 0, removed: 0, exist: 0
13:44:10  info   BimRevisionService::runBimCompare | end
13:44:10  info   BimRevisionService::uploadDataFiles | begin

핵심: stderr error 로그 직후에도 BimRevisionService::runBimCompare | enduploadDataFiles | begin 가 그대로 흘러간다. forge-agents 내부에서 처리된 비치명적 stderr 출력임이 증명된다.

추가로 status board 결과(scope: svc:cupixworks-any-bimrevision-agent::unknown)는 같은 시점에 동일 stderr 페이로드의 frame 단위로 19개 동반 클러스터가 함께 묶여 있음을 보여준다. 인시던트 ID 2026-06-24-svc-cupixworks-any-bimrevision-agent--unknown-1은 status resolved.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 ChildProcessManager.stderr 핸들러가 무조건 logger.error로 출력하여, 자식 안에서 catch된 stack trace까지 production error로 분류된다 packages/base/src/manager/child-process.manager.ts:138-141에서 stderr 청크 전체를 logger.error로 기록. 같은 메커니즘으로 AWS SDK v2 deprecation NOTE도 error 로그로 분류됨(2026-06-24 13:46:56, 13:47:03 KST). stderr 직후 runBimCompare | end가 정상 로깅됨 Confirmed
H2 Autodesk Forge 외부 의존성 장애 (404 발생 자체가 root cause) ForgeUtilsError: Resource not found ... AECModelData.json 404 로그 발생 forge-agents가 결과 객체의 Error/ErrorMessage 필드로 비교 결과를 정상 반환했고, `BimRevisionService::runBimCompare end로 끝나고uploadDataFiles
H3 자식 프로세스가 비정상 종료(exit code != 0)되어 BIM revision job 자체가 실패 ChildProcessManagerclose/exit 핸들러(child-process.manager.ts:107-127)는 별도의 Child process closed/Child process exited 로그를 남기는데, 분석 윈도우에는 해당 로그가 없음. runBimCompare | end 도달 사실로 child가 IPC 응답을 정상 반환했음이 확인됨 Rejected
H4 error-sweeper fingerprint 분류기가 같은 사건을 frame 단위로 과도하게 쪼개 한 사건이 다수 클러스터로 폭증 status board에서 동일 사건이 20개 cluster로 묶여 svc:cupixworks-any-bimrevision-agent::unknown 인시던트를 형성 본 클러스터의 root cause는 아니지만, 영향 폭(20개 cluster)을 만든 보조 요인 Confirmed (보조 요인)

Fix Recommendation#

즉시 조치 (Critical)#

없음. 사용자 워크플로에 영향 없는 stderr 노이즈이며, 실제 비교 결과는 forge-agents가 IPC로 정상 반환했다. 인시던트는 이미 auto-resolved.

단기 개선 (1주 이내)#

  • packages/base/src/manager/child-process.manager.ts:138-141 의 stderr 핸들러를 다음 방향으로 조정한다.
    • 자식의 stderr 출력은 기본적으로 warn 레벨로 격하한다. error 분류는 child의 exit code != 0 또는 process.on('error') 시그널에 한정한다.
    • (node:NN) NOTE: / (node:NN) DeprecationWarning: / (node:NN) ExperimentalWarning: 같은 Node.js 내장 경고 접두사는 info로 다운그레이드.
    • Stack / ForgeUtilsError: Resource not found 같이 forge-agents 가 IPC 결과로 정상 반환하는 케이스의 stderr는 child측에서 별도 채널(예: structured log_error IPC)로 전달하도록 forge-agents에 요청하거나, 부모에서 buffer 후 child가 정상 exit하면 디스카드한다.
  • BimRevisionService::runBimCompare(bim-revision-service.ts:660-672)에서 forgeErrorCode === 'API_NOT_FOUND' 인 경우 ForgeErrorMapper.ignorableErrorCodes에 추가할지 검토. 현재는 ROOM_EXT_ROOM_MAKER_IS_NOT_DEFINED만 ignorable. AEC manifest 부재가 비교 자체를 막지 않는다면 ignorable로 분류하여 명시적으로 warn 처리한다.

장기 개선 (재발 방지)#

  • 자식 프로세스 로그 정책을 명문화: stderr는 "프로세스가 죽었거나 IPC가 깨졌을 때만 error", 그 외에는 warn/info. 현재는 모든 자식 stderr가 error로 흘러 인시던트 검출이 흐려진다.
  • forge-agents에 stderr 직접 출력 대신 structured logger를 주입하거나, child가 결과 객체에 담아 IPC로만 보고하는 패턴을 강제. third-party SDK가 stderr로 deprecation notice를 출력하는 경우도 같은 방식으로 흡수.
  • error-sweeper fingerprint 분류기에서 stack frame 라인 단위 폭증을 방지하기 위해, "동일 메시지 prefix" 그룹화 룰(예: ChildProcessManager::setupEventHandlers | Child process stderr: prefix는 한 fingerprint로 묶기)을 검토.

Monitoring#

신규 Datadog 모니터링 쿼리:

text
sum:logs.hits{service:cupixworks-any-bimrevision-agent,status:error,@message:"ChildProcessManager::setupEventHandlers | Child process stderr"}.as_count()

stderr 폭증을 감지하는 별도 트리거. 단기 개선 후 이 카운트가 의미 있는 수준으로 줄어드는지 확인한다.

forge 404 실제 발생률(IPC 결과 기준)을 트래킹하려면:

text
sum:logs.hits{service:cupixworks-any-bimrevision-agent,status:warn,@message:"forgeErrorCode"}.as_count()

BimRevisionService::runBimCompare 가 처리한 forge 에러 코드 빈도. 404가 만성적이라면 Autodesk Forge integration 측 대응을 검토.

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard

사용자 영향 없는 로그 노이즈가 production error로 분류되는 문제이므로 즉시 코드 fix는 필요하지 않다. 그러나 같은 메커니즘이 향후 실제 장애 시그널을 덮어버릴 수 있으므로 단기 개선(자식 stderr → warn 격하, ignorable forge error 분류 확장)을 권장한다.