ES /docs

VoxelService::run | end - error: "Invalid potree_state"

RCA: VoxelService::run | end - error: "Invalid potree_state"

Overview#

What Happened#

2026-04-24 19:43 UTC에 cupixworks-any-voxel-agent 서비스에서 Pointcloud ID 1054099에 대한 voxel 처리 요청 시 potree_state"error"인 상태에서 SQS 메시지가 도착하여 validation 실패가 발생했다. 동일한 세션에서 약 2초 간격으로 2회 반복 발생했으며, 후속 voxel_state: "error" 업데이트 시도도 이미 에러 상태여서 400 응답을 받았다.

Quick Facts#

Field Value
exception.class Error
exception.message Invalid potree_state
top_frame cpreality-capture.ts:152
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
ipsdb (team 846) 2 Pointcloud 1054099 voxel 처리 실패, 사용자 awardigo@ipsdb.com의 포인트클라우드 voxel 결과 생성 불가

Timeline#

  1. 2026-04-24T19:43:48.031Z — 첫 번째 SQS 메시지 처리 시 CPRealityCapture::validate에서 potree_state: error 감지, Invalid potree_state 에러 throw
  2. 2026-04-24T19:43:50.645Z — 두 번째 SQS 메시지 처리 시 동일 에러 발생
  3. 2026-04-24T19:43:51ZBaseService::handlingMessageErrors에서 voxel_state: "error" PUT 시도 → Tesla API가 400 STAT40000 State not changed 반환
  4. 2026-04-25 — Error Sweeper가 클러스터 감지 및 RCA 수행

Error Log#

Datadog Logs

text
VoxelService::run | end - error: "Invalid potree_state"

Impact#

  • Service: cupixworks-any-voxel-agent
  • 발생 횟수: 2
  • 최초 발생: 2026-04-24T19:43:48.031Z
  • 최근 발생: 2026-04-24T19:43:50.645Z

이 에러는 14일간 4건(validate 로그 포함)만 발생했으며 모두 동일 Pointcloud ID 1054099에 대한 것이다. 단일 사용자/단일 포인트클라우드에 국한된 이슈로 시스템 전반 영향은 제한적이다. 다만 해당 사용자의 포인트클라우드 voxel 데이터가 생성되지 않아 3D 뷰에서 voxel 기반 기능(면적 측정, 공간 분석 등)을 사용할 수 없다.

Root Cause Summary#

Pointcloud 1054099의 potree_state"error" 상태인데도 voxel 처리 SQS 메시지가 큐에 존재하여 처리되었다. Voxel agent는 CPRealityCapture::validate()에서 potree_stateUploaded인지 검증하는데, 이 pointcloud는 이전 potree 변환 과정에서 실패하여 potree_state: error가 된 상태였다. 결국 voxel agent가 potree 데이터 없이 voxel을 계산할 수 없어 정상적인 validation 실패이다. 근본 원인은 potree 처리가 실패한 pointcloud에 대해 voxel 처리 메시지가 큐에 발행된 것이며, 이는 상위 워크플로우에서 potree 상태를 확인하지 않고 voxel 메시지를 전송했거나 potree 실패 후 메시지 취소가 이루어지지 않은 것이다.

Technical Analysis#

Code Path#

  • Entry point: BaseService::runByMessage (base-service.ts:153) — SQS 메시지 수신 후 run() 호출
  • VoxelService::run (voxel-service.ts:45) — targetId와 msgObject로 voxel 처리 시작
  • RealityCaptureManager::loadRealityCapture (reality_capture.manager.ts:39) — Tesla API에서 pointcloud 데이터 로드
  • RealityCaptureManager::createCPRealityCapture (reality_capture.manager.ts:69) — CPRealityCapture 생성 후 validate() 호출
  • Failure point: CPRealityCapture::validate (cpreality-capture.ts:150-153) — potree_state !== Uploaded 조건에서 throw

VoxelService::run은 SQS 메시지에서 받은 pointcloud ID로 Tesla API에서 모델 데이터를 로드한 후, createCPRealityCapture를 통해 validation을 수행한다:

packages/cupix-tesla-voxel-agent/src/voxel-service.ts:45-67typescript
run = async (targetId: number, msgObject?: any): Promise<void> => {
    const targetType = msgObject.type ?? 'capture';
    logger.debug('VoxelService::run | begin - targetId: %d, targetType: %s', targetId, targetType);
    try {
        const serverRealityCapture = await this.realityCaptureManager.loadRealityCapture(targetId, targetType);
        const cpRealityCapture = this.realityCaptureManager.createCPRealityCapture(serverRealityCapture, targetType);
        // ... voxel 처리 로직 ...
    } catch (error: any) {
        logger.error('VoxelService::run | error', error);
        if (!DEBUG_MODE) await this.realityCaptureManager.updateVoxelState(TESLA.VoxelState.Error);
    }
};

createCPRealityCapture에서 validate() 호출:

packages/cupix-tesla-voxel-agent/src/manager/reality_capture.manager.ts:69-79typescript
createCPRealityCapture = (srvRealityCapture: TESLA.Capture | TESLA.Pointcloud, targetType?: string): CPRealityCapture => {
    logger.debug('RealityCaptureManager::createCPRealityCapture | begin');
    if (!srvRealityCapture || !srvRealityCapture.id) {
        logger.error('RealityCaptureManager::createCPRealityCapture | end - invalid reality capture');
        throw new Error('Invalid srvRealityCapture');
    }
    const cpRealityCapture = new CPRealityCapture(srvRealityCapture, targetType);
    cpRealityCapture.validate();
    logger.debug('RealityCaptureManager::createCPRealityCapture | end');
    return cpRealityCapture;
};

Validation에서 pointcloud 타입일 때 potree_stateUploaded가 아니면 에러를 throw:

packages/cupix-tesla-voxel-agent/src/model/cpreality-capture.ts:144-153typescript
if (this.isPointcloud) {
    if (!this.potreeUrl) {
        logger.error('CPRealityCapture::validate | Invalid potreeUrl: %s for Pointcloud ID: %d', this.potreeUrl, this.id);
        throw new Error('Invalid potreeUrl');
    }

    if ((this._srvModel as TESLA.Pointcloud)?.potree_state !== TESLA.PointcloudPotreeState.Uploaded) {
        logger.error('CPRealityCapture::validate | Invalid potree_state: %s for Pointcloud ID: %d', (this._srvModel as TESLA.Pointcloud)?.potree_state, this.id);
        throw new Error('Invalid potree_state');
    }
}

PointcloudPotreeState enum 정의 (@tesla/typescript-node-sdk):

@tesla/typescript-node-sdk/model/pointcloudPotreeState.d.tstypescript
export declare enum PointcloudPotreeState {
    Created,
    Processing,
    Uploading,
    Missing,
    Uploaded,
    Error
}

Pointcloud 1054099의 potree_stateError(= potree 변환 실패)이므로 !== Uploaded 조건이 true가 되어 "Invalid potree_state" 에러가 발생했다. 이 에러는 catch 블록(voxel-service.ts:63-66)에서 잡혀 voxel_stateError로 업데이트 시도하지만, 이미 error 상태여서 Tesla API가 STAT40000 State not changed를 반환했다.

Log Evidence#

Datadog 쿼리로 전체 타임라인을 확인했다:

text
service:cupixworks-any-voxel-agent 1054099

시간순 전체 로그 (2026-04-24T19:43:48–51Z):

text
[19:43:48.031Z] ERROR CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
[19:43:48.031Z] ERROR VoxelService::run | end - error: "Invalid potree_state"
[19:43:50.645Z] ERROR CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
[19:43:50.645Z] ERROR VoxelService::run | end - error: "Invalid potree_state"
[19:43:51Z]     WARN  CupixAuth::handleError | Response statusCode: 400, requestUriHref: http://api-tesla.cupix.internal/api/v1/pointclouds/1054099?fields=..., body.result: {"code":"STAT40000","type":"Cupix::Errors::InvalidState","reason":"State not changed","message":"State not changed"}
[19:43:51Z]     WARN  BaseService::getApiErrorToDeleteMessage | error msg - {"modelId":1054099}
[19:43:51Z]     ERROR BaseService::handlingMessageErrors | Error and message object - {"error":{"message":"Request failed with status code 400",...,"data":"{\"voxel_state\":\"error\"}","url":"http://api-tesla.cupix.internal/api/v1/pointclouds/1054099?fields=..."},"sqsMessage":{"MessageId":"e59d4af9-0dbb-4865-9507-5994a6269672","Attributes":{"ApproximateReceiveCount":"1"}}}

핵심 증거:

  1. validate 로그에서 potree_state: error가 명시적으로 확인됨
  2. voxel_state: "error" PUT 요청에 대한 STAT40000 State not changed 응답 — voxel_state가 이미 error 상태였음
  3. 동일 세션 ID 6fa969e3a2757598843bfa5412acfa20819e1156에서 2건 발생 — 같은 배치에서 동일 메시지가 2회 처리됨
  4. SQS ApproximateReceiveCount: 1 — 첫 수신이므로 재시도가 아닌 원본 메시지 2건

Potree agent 로그 검색 (service:cupixworks-any-potree-agent 1054099, 24시간 범위)에서 결과가 없었다. 이는 potree 처리가 14일 이전에 실패했거나 로그 retention 범위를 벗어난 것을 의미한다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Potree 변환이 실패하여 potree_state: error인 pointcloud에 voxel 메시지가 발행됨 validate 로그에서 potree_state: error 명시 확인 (cpreality-capture.ts:151); Tesla API 400 응답에서 voxel_state 이미 error 상태 확인 Confirmed
H2 Voxel agent 코드의 validation 로직 버그로 정상 potree_state를 잘못 비교 PointcloudPotreeState enum이 정확히 정의되어 있고 (Uploaded = 4), 코드가 !== Uploaded 비교 수행; 로그에서 실제 potree_state 값이 error임을 확인 Rejected
H3 Tesla API 응답의 race condition으로 potree_state가 일시적으로 error로 보임 2초 간격 2회 요청 모두 동일하게 error 반환; voxel_state 업데이트 시에도 State not changed 반환 — 일시적이 아닌 지속적 상태 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 에러 레벨 조정 필요 없음: 이 에러는 정상적인 validation 실패로, 진짜 문제(potree 실패 pointcloud에 voxel 요청)를 감지하는 역할을 하고 있다.
  • 다만, voxel_state 업데이트 시 이미 error 상태인 경우의 400 응답을 별도 처리하여 불필요한 에러 로그를 줄일 수 있다. voxel-service.ts:65의 catch 블록에서 updateVoxelState 호출 전에 현재 voxel_state를 확인하거나, 400 STAT40000을 무시하도록 처리.

단기 개선 (1주 이내)#

  • 상위 워크플로우에서 voxel SQS 메시지 발행 전 potree_state 검증 추가: voxel 처리를 트리거하는 서비스(Tesla API의 worker 또는 워크플로우 오케스트레이터)에서 pointcloud의 potree_stateUploaded일 때만 voxel 메시지를 큐에 넣도록 가드 조건을 추가해야 한다.
  • Pointcloud 1054099의 potree 실패 원인을 확인하고, 필요시 potree 재처리 트리거.

장기 개선 (재발 방지)#

  • Voxel agent에서 potree_state !== Uploaded인 경우를 warn 레벨로 로깅하고 gracefully skip 처리하는 방안 검토. 현재는 error + throw로 처리되어 voxel_state: Error로 전환되는데, potree가 아직 진행 중(Processing, Uploading)인 경우에는 재시도가 적절할 수 있다.
  • 메시지 큐 발행과 상태 머신 간의 결합도를 높여, 특정 상태에서만 다음 단계 메시지가 발행되는 상태 기반 워크플로우 도입 검토.

Monitoring#

  • potree_stateerror인 상태에서 voxel 요청이 들어오는 빈도를 추적하는 메트릭 추가:
text
service:cupixworks-any-voxel-agent status:error "Invalid potree_state"
  • 14일 범위에서 4건으로 빈도가 낮지만, 반복 발생 시 상위 워크플로우의 상태 관리 로직 점검 필요.

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard — 상위 워크플로우의 메시지 발행 조건 수정이 필요하며, voxel agent 자체의 변경은 에러 레벨 조정 수준이다.