CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
RCA: CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
Overview#
What Happened#
2026-04-24 19:43 UTC에 cupixworks-any-voxel-agent 서비스에서 Pointcloud ID 1054099에 대한 voxel 계산 작업이 2회 실패했다. Pointcloud의 potree_state가 uploaded가 아닌 error 상태였기 때문에 CPRealityCapture::validate에서 사전 조건 검증에 실패한 것이다. 이전 potree 처리 파이프라인에서 해당 pointcloud의 potree_state가 error로 전환된 직후, voxel-agent가 SQS 메시지를 수신하여 처리를 시도한 것이 원인이다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error |
| exception.message | Invalid potree_state |
| top_frame | cpreality-capture.ts:150 |
| env | production, us-west-2 |
Timeline#
- 04:34:04 KST — Potree-agent가 Pointcloud 1054099 처리 시작 (download, entity_parameters 요청)
- 04:39:19 KST — Pointcloud 상태 queued → done 전환 (첫 번째 처리 완료)
- 04:39:39 KST — 두 번째 처리 완료, 다시 queued → done 전환
- 04:40:35 KST — Potree upload credentials 요청 실패 (400,
potree_state is uploaded state) - 04:40:39 KST — Pointcloud 상태 done → error 전환
- 04:41:05 KST — PUT update (potree-agent 재처리 시작)
- 04:43:01 KST — Potree upload credentials 요청 (potree-agent 업로드 중)
- 04:43:48 KST — Voxel-agent가 SQS 메시지 수신,
CPRealityCapture::validate실패 (potree_state: error) - 04:43:50 KST — 동일 에러 두 번째 발생 (SQS 메시지 재처리)
- 04:43:51 KST — Voxel-agent가 voxel_state를 error로 업데이트 시도 → 400 "State not changed" (이미 error 상태)
- 04:56:44~47 KST — Potree-agent가 정상 완료 (check_uploading, update, meta/potree, meta/prop)
Error Log#
CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
Impact#
- Service:
cupixworks-any-voxel-agent - 발생 횟수: 2
- 최초 발생: 2026-04-24T19:43:48.031Z
- 최근 발생: 2026-04-24T19:43:50.644Z
Root Cause Summary#
Voxel-agent가 Pointcloud 1054099에 대한 voxel 계산 SQS 메시지를 수신했으나, 해당 pointcloud의 potree_state가 아직 uploaded 상태가 아닌 error 상태였다. 이는 potree 처리 파이프라인에서 potree_state가 done → error로 전환된 직후(04:40:39), potree-agent가 재처리를 시작했지만(04:41:05) 아직 완료하지 못한 상태에서 voxel-agent SQS 메시지가 도착했기 때문이다. CPRealityCapture::validate는 potree_state === Uploaded만 허용하므로 error 상태에서 즉시 예외를 발생시켰다. 이는 파이프라인 간 조율(orchestration) 부재로 인한 race condition이다 — voxel 처리가 potree 처리 완료를 대기하지 않고 독립적으로 트리거된다.
Technical Analysis#
Code Path#
- Entry point:
voxel-service.ts:45—VoxelService::run메서드가 SQS 메시지로 트리거됨 reality_capture.manager.ts:39-67—loadRealityCapture가 Tesla API에서 pointcloud 데이터 로드reality_capture.manager.ts:69-79—createCPRealityCapture가CPRealityCapture객체 생성 후validate()호출- Failure point:
cpreality-capture.ts:150—potree_state !== TESLA.PointcloudPotreeState.Uploaded검증 실패
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);
if (!DEBUG_MODE) await this.realityCaptureManager.updateVoxelState(TESLA.VoxelState.Aggregating);
// ... voxel 계산 로직 ...
} catch (error: any) {
logger.error('VoxelService::run | error', error);
if (!DEBUG_MODE) await this.realityCaptureManager.updateVoxelState(TESLA.VoxelState.Error);
}
};
validate() 호출 시점에서 pointcloud의 potree_state가 error이므로, TESLA.PointcloudPotreeState.Uploaded (enum value 4)와 일치하지 않아 예외가 발생한다:
validate = (): boolean => {
logger.debug('CPRealityCapture::validate | begin - id: %d', this.id);
if (this.id == Constants.UnknownId) {
logger.error('CPRealityCapture::validate | Invalid ID: %d', this.id);
throw new Error('Invalid ID');
}
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');
}
}
return true;
};
catch 블록에서 updateVoxelState(TESLA.VoxelState.Error)를 호출하지만, Tesla API에서 이미 voxel_state가 error인 경우 STAT40000 "State not changed" 에러를 반환하여 이차 에러가 발생한다:
when 'error'
raise Cupix::Errors::InvalidState.new(code: 'STAT40000', reason: 'State not changed') if @model.voxel_state_error?
@model.error_voxel_state!
Log Evidence#
Datadog 쿼리:
service:cupixworks-any-voxel-agent status:error "CPRealityCapture::validate"
service:cupixworks-any-voxel-agent "1054099"
(service:cupixworks-any-potree-agent OR service:cupixworks-api) "1054099"
Voxel-agent 에러 로그 (2건):
2026-04-25 04:43:50 KST | error | CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
2026-04-25 04:43:48 KST | error | CPRealityCapture::validate | Invalid potree_state: error for Pointcloud ID: 1054099
Voxel-agent의 error 상태 업데이트 실패:
{
"timestamp": "2026-04-25 04:43:51 KST",
"status": "warn",
"message": "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\"}"
}
Pointcloud 상태 전환 이력 (Tesla API 로그):
2026-04-25 04:39:19 KST | info | pointcloud state changed from queued to done. id: 1054099
2026-04-25 04:40:35 KST | info | [400] POST potree_upload_credentials - "potree_state is uploaded state"
2026-04-25 04:40:39 KST | info | pointcloud state changed from done to error. id: 1054099
2026-04-25 04:43:49 KST | info | [200] PUT /api/v1/pointclouds/1054099 (update)
2026-04-25 04:43:51 KST | info | [400] PUT /api/v1/pointclouds/1054099 - "State not changed"
2026-04-25 04:56:46 KST | info | [200] PUT /api/v1/pointclouds/1054099 (potree 재처리 완료)
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Voxel-agent SQS 메시지가 potree 처리 완료 전에 도착하여 race condition 발생 | 04:40:39에 done→error 전환 후 04:43:48에 voxel-agent가 validate 실행. Potree-agent는 04:56:46에 완료. 로그 타임라인이 명확히 비동기 트리거를 보여줌 | — | Confirmed |
| H2 | Potree 처리 자체가 영구적으로 실패하여 pointcloud가 error 상태에 고착 | 04:40:35에 potree_state is uploaded state 에러 후 done→error 전환 |
04:56:46에 potree-agent가 정상 완료함 (200 응답). Error 상태는 일시적이었음 | Rejected |
| H3 | Voxel-agent의 validate 로직 버그로 인해 정상 상태를 잘못 판단 | — | potree_state가 실제로 error였음이 로그로 확인됨. Validate 로직 자체는 정확하게 uploaded 상태만 허용 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
packages/cupix-tesla-voxel-agent/src/model/cpreality-capture.ts:150-153—potree_state가uploaded가 아닌 경우 에러 대신 재시도 가능한 예외를 throw하거나, warn 레벨 로그를 남기고 SQS 메시지를 재큐잉하는 방식으로 변경 검토. 현재는 error로 로깅하고voxel_state를 error로 설정하여 불필요한 실패 처리가 된다.packages/cupix-tesla-voxel-agent/src/voxel-service.ts:65—updateVoxelState(Error)호출 시 이미 error 상태인 경우의STAT40000응답을 무시하도록 에러 핸들링 추가 필요. 현재 이 이차 에러가BaseService::handlingMessageErrors까지 전파되어 추가 에러 로그를 생성한다.
단기 개선 (1주 이내)#
- Voxel-agent에 potree_state 확인 시
error또는 비-uploaded상태에 대해 재시도 로직(backoff + SQS visibility timeout 연장)을 추가하여, potree 처리가 아직 진행 중인 경우 일정 시간 후 재처리하도록 개선. validate()실패 시 에러 레벨을warn으로 낮추는 것을 검토. 이는 실제 버그가 아닌 파이프라인 타이밍 이슈이므로error레벨이 과도하다.
장기 개선 (재발 방지)#
- Potree 처리와 voxel 처리 간의 의존성을 명시적으로 관리하는 오케스트레이션 레이어 도입. 현재 두 에이전트가 독립적인 SQS 큐로 비동기 트리거되므로, potree 처리 완료 이벤트가 voxel 처리를 트리거하는 이벤트 기반 파이프라인으로 전환 필요.
Monitoring#
- Voxel-agent에서
Invalid potree_state에러 빈도를 추적하는 메트릭 추가 - Datadog 쿼리:
service:cupixworks-any-voxel-agent "Invalid potree_state" status:error
- Potree 처리 완료와 voxel 처리 시작 간의 시간 차이를 메트릭으로 수집하여, race condition 빈도를 모니터링
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- Potree-agent가 재처리를 완료하면 (04:56:46에 확인) pointcloud는 정상 상태로 복구된다. 사용자 데이터 손실은 없으며, voxel 처리만 일시적으로 실패한다. 다만 voxel 처리가 자동 재시도되지 않으므로, 수동 재트리거가 필요할 수 있다.