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#
- 08:08:32Z -- Voxel agent 인스턴스 초기화 (ip-10-1-96-18)
- 08:09:00Z -- CupixAuth 세션 인증 완료
- 08:10:00.542Z -- Tesla API GET
/api/v1/captures/685954504 Gateway Timeout 반환 - 08:10:00.543Z --
loadRealityCapture실패,_realityCaptureId/_realityCaptureType미설정 - 08:10:00.544Z --
VoxelService::runcatch 블록 진입 - 08:10:00.549Z --
updateVoxelState(Error)호출 시 undefined 필드 검증 실패 (본 에러) - 08:10:00.554Z -- SQS 메시지 삭제됨 (에러에도 불구하고)
Error Log#
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 호출이 실패했다. RealityCaptureManager는 loadRealityCapture 성공 시에만 _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이 설정된다:
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 호출 성공 후에만 내부 상태를 설정한다:
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한다:
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');
}
// ...
};
생성자에서도 이 필드들은 전달되지 않는다:
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 쿼리:
service:cupixworks-any-voxel-agent status:error @environment:production
service:cupixworks-any-voxel-agent "RealityCaptureManager" @environment:production
동일 호스트(ip-10-1-96-18)에서의 이벤트 시퀀스:
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
08:10:00.543Z [error] RealityCaptureManager::loadRealityCapture | end - "Request failed with status code 504"
08:10:00.544Z [error] VoxelService::run | end - error: "Request failed with status code 504"
08:10:00.549Z [error] RealityCaptureManager::updateVoxelState | end - undefined reality capture id or type
catch 블록에서 updateVoxelState(Error) 실패 후 Tesla API에 voxel_state 업데이트를 시도하지만 이것도 504로 실패하는 패턴:
08:10:00.551Z [warn] BaseService::getApiErrorToDeleteMessage | undefined response - {}
{
"error": "undefined response",
"sqsMessage": {
"MessageId": "dcbec964-6874-4681-8017-53042c1515b3",
"Attributes": {
"ApproximateReceiveCount": "1"
}
}
}
동일 시간대 다른 호스트들에서도 504 에러가 광범위하게 발생:
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호출 전에realityCaptureId와realityCaptureType이 설정되어 있는지 확인하는 guard를 추가해야 한다.loadRealityCapture실패 시에는updateVoxelState를 호출하지 않거나,targetId와targetType을 직접 사용하여 Tesla API에 에러 상태를 기록하는 대안 경로를 구현해야 한다.
단기 개선 (1주 이내)#
updateVoxelState가 내부 상태 대신 인자로realityCaptureId와realityCaptureType을 받을 수 있도록 메서드 시그니처를 개선하는 것을 검토한다. 이렇게 하면loadRealityCapture실패 후에도 SQS 메시지에서 추출한targetId/targetType으로 에러 상태를 기록할 수 있다.loadRealityCapture실패 시 504 Gateway Timeout에 대해 retry 로직 추가를 검토한다.
장기 개선 (재발 방지)#
- Tesla API의 504 timeout이 동시간대에 광범위하게 발생한 원인을 조사해야 한다. 7개 이상의 인스턴스에서 동시에 504가 발생한 것은 Tesla API 측의 부하 또는 인프라 문제를 시사한다.
- voxel agent의 에러 핸들링 패턴 전반을 검토하여 "에러 복구 중 2차 에러 발생" 패턴이 다른 곳에도 존재하는지 확인한다.
Monitoring#
updateVoxelStateundefined 에러 재발 모니터링:
service:cupixworks-any-voxel-agent status:error "updateVoxelState" "undefined reality capture id or type"
- Tesla API 504 timeout 빈도 모니터링:
service:cupixworks-any-voxel-agent "statusCode: 504"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial -- catch 블록에 guard 추가 또는
updateVoxelState메서드 시그니처 변경으로 해결 가능. 본 에러 자체는 504 timeout의 2차 효과이며, 메시지 처리 실패 시 SQS에서 재시도되므로 데이터 손실 위험은 낮다.