ES /docs

RealityCaptureManager::updateVoxelState | end - undefined reality capture id or type

RCA: RealityCaptureManager::updateVoxelState | end - undefined reality capture id or type

Overview#

What Happened#

2026-04-23 08:10:00 UTC에 cupixworks-any-voxel-agent 서비스에서 capture 685954에 대한 voxel 처리 중 Tesla API(api-tesla.cupix.internal)로부터 504 Gateway Timeout이 발생했다. loadRealityCapture가 실패하면서 내부 상태(_realityCaptureId, _realityCaptureType)가 설정되지 않았고, catch 블록에서 에러 상태를 기록하려는 updateVoxelState(Error) 호출이 undefined 필드 검증에 걸려 2차 에러가 발생했다.

Quick Facts#

Field Value
exception.message undefined reality capture id or type
top_frame reality_capture.manager.ts:84
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
qatest3 1 capture 685954 voxel 처리 실패, voxel_state가 error로 전이되지 않아 상태 불일치 가능

Timeline#

  1. 08:08:32Z -- Voxel agent 인스턴스 초기화 (ip-10-1-96-18)
  2. 08:09:00Z -- CupixAuth 세션 인증 완료
  3. 08:10:00.542Z -- Tesla API GET /api/v1/captures/685954 504 Gateway Timeout 반환
  4. 08:10:00.543Z -- loadRealityCapture 실패, _realityCaptureId/_realityCaptureType 미설정
  5. 08:10:00.544Z -- VoxelService::run catch 블록 진입
  6. 08:10:00.549Z -- updateVoxelState(Error) 호출 시 undefined 필드 검증 실패 (본 에러)
  7. 08:10:00.554Z -- SQS 메시지 삭제됨 (에러에도 불구하고)

Error Log#

Datadog Logs

text
RealityCaptureManager::updateVoxelState | end - undefined reality capture id or type

Impact#

  • Service: cupixworks-any-voxel-agent
  • 발생 횟수: 1
  • 최초 발생: 2026-04-23T08:10:00.549Z
  • 최근 발생: 2026-04-23T08:10:00.549Z

동일 시간대에 504 관련 에러가 20건 이상 발생했으며(capture 685930, 685931, 685932, 685941, 685954, 685176, 685560), 7개 이상의 EC2 인스턴스와 여러 팀(qatest3, devcon, etro)에 영향을 미쳤다. 이 클러스터의 특정 에러(updateVoxelState undefined)는 1건이지만, 근본 원인인 504 timeout은 동일 시간대에 광범위하게 발생했다.

Root Cause Summary#

Tesla API(api-tesla.cupix.internal)가 504 Gateway Timeout을 반환하면서 loadRealityCapture 호출이 실패했다. RealityCaptureManagerloadRealityCapture 성공 시에만 _realityCaptureId_realityCaptureType을 설정하는데, 504 에러로 인해 이 필드들이 undefined 상태로 남았다. VoxelService::run의 catch 블록에서 에러 복구를 위해 updateVoxelState(TESLA.VoxelState.Error)를 호출했으나, 해당 메서드의 guard clause(if (!this.realityCaptureId || !this.realityCaptureType))에서 undefined 값을 감지하여 2차 에러를 발생시켰다. 이는 에러 핸들링 경로에서 loadRealityCapture 실패 시나리오를 고려하지 않은 코드 설계 결함이다.

Technical Analysis#

Code Path#

  • Entry point: voxel-service.ts:45 -- VoxelService.run()
  • loadRealityCapture 호출: voxel-service.ts:49
  • 실패 시 catch 블록: voxel-service.ts:63-66
  • Failure point: reality_capture.manager.ts:83-85

VoxelService.run()에서 loadRealityCapture가 먼저 호출되고, 성공 시 _realityCaptureId_realityCaptureType이 설정된다:

voxel-service.ts:45-67typescript
run = async (targetId: number, msgObject?: any): Promise<void> => {
	const targetType = msgObject.type ?? 'capture';
	try {
		const serverRealityCapture = await this.realityCaptureManager.loadRealityCapture(targetId, targetType);
		// ... 정상 처리 ...
		if (!DEBUG_MODE) await this.realityCaptureManager.updateVoxelState(TESLA.VoxelState.Aggregating);
		// ...
	} catch (error: any) {
		logger.error('VoxelService::run | error', error);
		if (!DEBUG_MODE) await this.realityCaptureManager.updateVoxelState(TESLA.VoxelState.Error);  // 여기서 2차 에러 발생
	}
};

loadRealityCapture는 API 호출 성공 후에만 내부 상태를 설정한다:

reality_capture.manager.ts:39-67typescript
loadRealityCapture = (realityCaptureId: number, realityCaptureType: string): Promise<TESLA.Capture | TESLA.Pointcloud> => new Promise((resolve, reject) => {
	getRealityCapture(realityCaptureId)
		.then(srvRealityCapture => {
			// ...
			this._realityCaptureId = realityCaptureId;      // 성공 시에만 설정
			this._realityCaptureType = realityCaptureType;   // 성공 시에만 설정
			resolve(srvRealityCapture);
		})
		.catch(ec => {
			logger.error('RealityCaptureManager::loadRealityCapture | end - %s', JSON.stringify(ec.message));
			reject(ec);  // _realityCaptureId, _realityCaptureType 미설정 상태로 reject
		});
});

updateVoxelState의 guard clause에서 undefined 필드를 감지하여 에러를 throw한다:

reality_capture.manager.ts:81-86typescript
updateVoxelState = async (state: TESLA.VoxelState): Promise<void> => {
	if (!this.realityCaptureId || !this.realityCaptureType) {
		logger.error('RealityCaptureManager::updateVoxelState | end - undefined reality capture id or type');
		throw new Error('Invalid realityCaptureId or realityCaptureType');
	}
	// ...
};

생성자에서도 이 필드들은 전달되지 않는다:

voxel-service.ts:18typescript
this._realityCaptureManager = new RealityCaptureManager(this.cupixApi);  // ID/Type 없이 생성

기대 동작: catch 블록에서 updateVoxelState(Error)를 호출하여 Tesla API에 voxel_state를 "error"로 업데이트해야 한다. 실제 동작: loadRealityCapture 실패로 _realityCaptureId가 undefined이므로 guard clause에서 차단되어 2차 에러 throw.

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-any-voxel-agent status:error @environment:production
text
service:cupixworks-any-voxel-agent "RealityCaptureManager" @environment:production

동일 호스트(ip-10-1-96-18)에서의 이벤트 시퀀스:

text
08:10:00.542Z [warn] CupixAuth::handleError | Response statusCode: 504, requestUriHref: http://api-tesla.cupix.internal/api/v1/captures/685954?fields=id,name,state,meta,record,level,bim_icp_tm,use_bim_icp_tm,voxel_state,voxels_result_urls,created_at, body.result: undefined
text
08:10:00.543Z [error] RealityCaptureManager::loadRealityCapture | end - "Request failed with status code 504"
text
08:10:00.544Z [error] VoxelService::run | end - error: "Request failed with status code 504"
text
08:10:00.549Z [error] RealityCaptureManager::updateVoxelState | end - undefined reality capture id or type

catch 블록에서 updateVoxelState(Error) 실패 후 Tesla API에 voxel_state 업데이트를 시도하지만 이것도 504로 실패하는 패턴:

text
08:10:00.551Z [warn] BaseService::getApiErrorToDeleteMessage | undefined response - {}
json
{
  "error": "undefined response",
  "sqsMessage": {
    "MessageId": "dcbec964-6874-4681-8017-53042c1515b3",
    "Attributes": {
      "ApproximateReceiveCount": "1"
    }
  }
}

동일 시간대 다른 호스트들에서도 504 에러가 광범위하게 발생:

text
08:10:23.898Z [error] VoxelService::run | end - error: "Request failed with status code 504" (ip-10-1-163-184, capture 685930)
08:10:00.927Z [error] VoxelService::run | end - error: "Request failed with status code 504" (ip-10-1-99-110, capture 685941)
08:38:39.972Z [error] VoxelService::run | end - error: "Request failed with status code 504" (ip-10-1-110-110, capture 685560)

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 loadRealityCapture 504 실패로 _realityCaptureId/_realityCaptureType 미설정 상태에서 catch 블록의 updateVoxelState 호출이 guard clause에서 차단됨 로그 시퀀스: 504 warn (08:10:00.542Z) -> loadRealityCapture 실패 (08:10:00.543Z) -> updateVoxelState undefined 에러 (08:10:00.549Z). 코드: reality_capture.manager.ts:58-59에서 성공 시에만 필드 설정. voxel-service.ts:65에서 catch 블록 내 updateVoxelState 호출 -- Confirmed
H2 SQS 메시지에 id 또는 type 필드가 누락되어 처음부터 undefined 로그에서 capture ID 685954가 정상적으로 인식됨 (setLogMeta에서 capture.id: 685954 기록). loadRealityCapture에 targetId가 정상 전달됨 SQS 메시지 파싱은 정상 작동. 504 에러가 API 호출 단계에서 발생한 것이 확인됨 Rejected
H3 RealityCaptureManager 생성자에서 ID/Type이 전달되지 않아 발생 voxel-service.ts:18에서 new RealityCaptureManager(this.cupixApi) -- 인자 없이 생성 정상 흐름에서는 loadRealityCapture 성공 시 필드가 설정되므로 생성자 인자 부재 자체는 문제가 아님. 504 실패가 근본 원인 Rejected (기여 요인)

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: voxel-service.ts:63-66
  • catch 블록에서 updateVoxelState 호출 전에 realityCaptureIdrealityCaptureType이 설정되어 있는지 확인하는 guard를 추가해야 한다. loadRealityCapture 실패 시에는 updateVoxelState를 호출하지 않거나, targetIdtargetType을 직접 사용하여 Tesla API에 에러 상태를 기록하는 대안 경로를 구현해야 한다.

단기 개선 (1주 이내)#

  • updateVoxelState가 내부 상태 대신 인자로 realityCaptureIdrealityCaptureType을 받을 수 있도록 메서드 시그니처를 개선하는 것을 검토한다. 이렇게 하면 loadRealityCapture 실패 후에도 SQS 메시지에서 추출한 targetId/targetType으로 에러 상태를 기록할 수 있다.
  • loadRealityCapture 실패 시 504 Gateway Timeout에 대해 retry 로직 추가를 검토한다.

장기 개선 (재발 방지)#

  • Tesla API의 504 timeout이 동시간대에 광범위하게 발생한 원인을 조사해야 한다. 7개 이상의 인스턴스에서 동시에 504가 발생한 것은 Tesla API 측의 부하 또는 인프라 문제를 시사한다.
  • voxel agent의 에러 핸들링 패턴 전반을 검토하여 "에러 복구 중 2차 에러 발생" 패턴이 다른 곳에도 존재하는지 확인한다.

Monitoring#

  • updateVoxelState undefined 에러 재발 모니터링:
text
service:cupixworks-any-voxel-agent status:error "updateVoxelState" "undefined reality capture id or type"
  • Tesla API 504 timeout 빈도 모니터링:
text
service:cupixworks-any-voxel-agent "statusCode: 504"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial -- catch 블록에 guard 추가 또는 updateVoxelState 메서드 시그니처 변경으로 해결 가능. 본 에러 자체는 504 timeout의 2차 효과이며, 메시지 처리 실패 시 SQS에서 재시도되므로 데이터 손실 위험은 낮다.