ES /docs

MaskWork#maskPanos stderr를 error로 잘못 분류 — 로깅 레벨 오류

RCA: MaskWork::maskPanos | RuntimeWarning: overflow encountered in exp

Error Log#

Datadog Logs

text
MaskWork::maskPanos | script /tmp/lib/face-body-detector/cupix_inference.py:266: RuntimeWarning: overflow encountered in exp
  sigmoid_mask = 1.0 / (1.0 + np.exp(-logits_resized))

Impact#

  • Service: cupixworks-pano-postprocessor-instance
  • Team: bnbuilders
  • 발생 횟수: 1 (클러스터 기준; 실제로는 동일 시간대에 20건 이상 반복 발생)
  • 최초 발생: 2026-04-20T01:43:15.001Z
  • 최근 발생: 2026-04-20T01:43:15.001Z

Root Cause Summary#

Python face-body-detector 스크립트의 sigmoid 연산에서 np.exp(-logits_resized) 호출 시 logits_resized 값이 매우 큰 음수일 때 -logits_resized가 매우 큰 양수가 되어 np.exp() 결과가 float64 범위를 초과하면서 RuntimeWarning: overflow encountered in exp가 발생한다. 이 경고는 Python의 warnings 모듈을 통해 stderr로 출력되며, Node.js 측 MaskWork::maskPanos 메서드가 subprocess의 stderr 출력을 모두 logger.error로 기록하기 때문에 Datadog에 error 레벨로 수집된다. 실제로는 NumPy가 overflow 시 inf를 반환하고 sigmoid 결과는 0.0으로 수렴하므로 마스킹 결과에 기능적 영향은 없으며, 프로세스는 exit code 0으로 정상 종료한다. 이 에러는 경고(warning)를 에러(error)로 잘못 분류한 로깅 문제이다.

Technical Analysis#

Code Path#

  • Entry point: pano-postprocessor-service.ts:126maskWork.maskPanos() 호출
  • MaskWork::maskPanos (mask-work.ts:23-59) — Python 스크립트를 child process로 실행
  • BlurScriptPath = /tmp/lib/face-body-detector/run.sh (constants.ts:5)
  • Python 스크립트 cupix_inference.py:266 — sigmoid 연산에서 overflow 발생
  • stderr 핸들러 (mask-work.ts:40-42) — 모든 stderr 출력을 logger.error로 기록

MaskWork::maskPanos에서 Python subprocess를 생성하고 stderr를 처리하는 부분:

typescript
// mask-work.ts:29-42
const process: ChildProcessWithoutNullStreams = spawn('stdbuf', ['-oL', 'bash', BlurScriptPath], {
    cwd: path.dirname(BlurScriptPath),
    stdio: 'pipe',
    env: { INPUT_DIRPATH: inputDir, OUTPUT_DIRPATH: outputDir, OPTION_FILE_PATH: optionPath }
});

process.stdout.on('data', (data) => {
    logger.debug('MaskWork::maskPanos | script %s', data.toString());
});

process.stderr.on('data', (data) => {
    logger.error('MaskWork::maskPanos | script %s', data.toString());  // 문제: stderr를 무조건 error로 분류
});

Python 측 문제 코드 (외부 의존성, 레포에 포함되지 않음):

python
# /tmp/lib/face-body-detector/cupix_inference.py:266
sigmoid_mask = 1.0 / (1.0 + np.exp(-logits_resized))

logits_resized에 큰 음수 값(-logits가 큰 양수)이 포함될 때 np.exp()float64 최대값(~1.8e308)을 초과하여 inf를 반환하고, Python RuntimeWarning이 stderr로 출력된다. 그러나 1.0 / (1.0 + inf) = 0.0이므로 sigmoid 결과값 자체는 수학적으로 올바르다 (해당 픽셀의 마스크 확률 ≈ 0).

프로세스 종료 처리:

typescript
// mask-work.ts:49-57
process.on('close', (code) => {
    if (code === 0) {
        logger.debug('MaskWork::maskPanos | success code:%d', code);
        resolve(true);
    } else {
        logger.error('MaskWork::maskPanos | error code:%d', code);
        reject(false);
    }
});

프로세스는 exit code 0으로 정상 종료하므로 resolve(true)가 호출되고, 이후 change_masks, markMaskEnhancement 등 후속 단계가 정상 실행된다.

Log Evidence#

Datadog 쿼리 (overflow 에러):

text
service:cupixworks-pano-postprocessor-instance status:error "MaskWork::maskPanos"

동일 시간대에 overflow 외에도 UserWarning (PyTorch kernel caching)이 함께 stderr로 출력되어 error로 기록됨:

text
MaskWork::maskPanos | script /tmp/lib/face-body-detector/sam3/model/encoder.py:358: UserWarning: No PYTORCH_KERNEL_CACHE_PATH or HOME environment variable set! This disables kernel caching. (Triggered internally at /pytorch/aten/src/ATen/native/cuda/jit_utils.cpp:1426.)
  spatial_shapes.prod(1).cumsum(0)[:-1],

프로세스가 정상 완료됨을 보여주는 로그:

Datadog 쿼리:

text
service:cupixworks-pano-postprocessor-instance "elapsed"
text
2026-04-20 10:58:45 KST | PanoPostprocessorService Elapsed Time - dd_step:blur_panos&dd_elapsed_time:383681ms
2026-04-20 10:58:51 KST | PanoPostprocessorService Elapsed Time - dd_step:change_masks&dd_elapsed_time:5876ms

blur_panos 단계가 정상 완료(383초)된 후 change_masks 단계도 성공적으로 실행됨. run | end 로그도 info 레벨로만 기록되고, error 레벨의 run | end 로그는 없음:

Datadog 쿼리:

text
service:cupixworks-pano-postprocessor-instance "PanoPostprocessorService::run | end" status:error

결과: 0건 — 에러로 종료된 run이 없음.

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: applications/agents/packages/cupix-pano-postprocessor/src/work/mask-work.ts:40-42
  • stderr 출력을 무조건 logger.error로 기록하는 대신 logger.warn으로 변경. Python의 RuntimeWarningUserWarning은 경고 수준이므로 error 레벨이 아닌 warn 레벨이 적절함. 이렇게 하면 Datadog error 알림에서 불필요한 노이즈가 제거됨.

단기 개선 (1주 이내)#

  • Python 스크립트(cupix_inference.py)에서 np.exp 호출 전에 입력값을 클리핑하거나 scipy.special.expit (numerically stable sigmoid)를 사용하도록 변경. 예: np.clip(-logits_resized, -500, 500) 적용 후 np.exp 호출. 이렇게 하면 warning 자체가 발생하지 않음.
  • PyTorch kernel caching warning 해결: Docker 컨테이너 환경에서 PYTORCH_KERNEL_CACHE_PATH 환경변수를 설정하여 UserWarning 제거.

장기 개선 (재발 방지)#

  • stderr 파싱 로직을 개선하여 Python warning(RuntimeWarning, UserWarning)과 실제 에러를 구분. 예: stderr 출력에 Warning: 패턴이 포함되면 logger.warn, 그 외는 logger.error로 분류.
  • face-body-detector 의존성 업데이트 시 numerically stable sigmoid 구현이 포함되었는지 확인하는 CI 검증 추가.

Monitoring#

  • 기존 Datadog 쿼리로 실제 에러만 모니터링:
text
service:cupixworks-pano-postprocessor-instance status:error "MaskWork::maskPanos | error code"
  • stderr warning을 warn 레벨로 변경한 후 warning 빈도 추적:
text
service:cupixworks-pano-postprocessor-instance status:warn "RuntimeWarning"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial
  • 이 에러는 기능적 영향이 없는 Python RuntimeWarning이 Node.js stderr 핸들러에 의해 error로 잘못 분류된 것. 마스킹 프로세스는 정상 완료되며 결과물에 영향 없음. 수정은 로그 레벨 변경(1줄)으로 충분.