VoxelService::run | end - error: "Invalid potreeUrl"
RCA: VoxelService::run | end - error: "Invalid potreeUrl"
Overview#
What Happened#
2026-05-12 06:35 UTC, cupixworks-any-voxel-agent 서비스(ap-southeast-2 리전)에서 Pointcloud ID 204434에 대한 voxel 계산 작업이 "Invalid potreeUrl" 에러로 실패했다. 해당 pointcloud의 potree_url 필드가 비어있는 상태에서 voxel 계산 요청이 전달된 것이 원인이다.
Quick Facts#
| Field | Value |
|---|---|
| exception.message | Invalid potreeUrl |
| top_frame | cpreality-capture.ts:147 |
| env | production, ap-southeast-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| endeavourgroup (team ID: 180) | 1 | Pointcloud ID 204434의 voxel 계산 실패, voxel_state가 Error로 전환 |
Timeline#
- 2026-05-12T06:35:44.170Z — Voxel agent가 SQS 메시지를 수신하여 Pointcloud 204434 처리 시작
- 2026-05-12T06:35:44.170Z —
CPRealityCapture::validate에서 potreeUrl이 빈 문자열임을 감지, "Invalid potreeUrl" 에러 throw - 2026-05-12T06:35:44.170Z —
VoxelService::run의 catch 블록이 에러를 로깅하고 voxel_state를 Error로 업데이트
Error Log#
VoxelService::run | end - error: "Invalid potreeUrl"
Impact#
- Service:
cupixworks-any-voxel-agent - 발생 횟수: 1
- 최초 발생: 2026-05-12T06:35:44.170Z
- 최근 발생: 2026-05-12T06:35:44.170Z
Root Cause Summary#
Pointcloud ID 204434의 potree_url이 null/빈 문자열인 상태에서 voxel 계산 SQS 메시지가 전달되었다. Rails 측 Cupix::VoxelService.calculate_voxels! 메서드는 potree_state 검증을 수행하지 않고 SQS 메시지를 전송한다. potree_state 검증은 check_calculate_voxels! 메서드에만 존재하며, refresh_voxels!를 통해 개별 pointcloud에 직접 호출하는 경우 이 검증이 우회된다. 결과적으로 potree 파일이 아직 업로드되지 않은(또는 revision이 0인) pointcloud에 대해 voxel 계산이 시도되어 agent 측 validation에서 실패한 것이다.
Technical Analysis#
Code Path#
- Entry point:
voxel-service.ts:45—VoxelService.run()메서드가 SQS 메시지로부터 targetId와 targetType을 수신 reality_capture.manager.ts:39-67—loadRealityCapture()가 cupixApi를 통해 pointcloud 데이터를 로드reality_capture.manager.ts:69-79—createCPRealityCapture()가 서버 모델로CPRealityCapture인스턴스 생성 후validate()호출- Failure point:
cpreality-capture.ts:144-148—validate()메서드에서 pointcloud의potreeUrl이 빈 문자열인지 검증
get potreeUrl(): string { return (this._srvModel as TESLA.Pointcloud)?.potree_url ?? ''; }
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');
}
Rails 측에서 SQS로 메시지를 보내는 과정:
def calculate_voxels!(model: nil, session: nil, **kwargs)
return unless Cupix::Tesla.launch_mode == 'CUPIXWORKS'
raise ... if model.blank?
raise ... unless model.is_a?(::Capture) || model.is_a?(::Pointcloud)
raise ... if model.error_code.present?
model.queued_voxel_state! if model.has_attribute?(:voxel_state)
invoke(model: model, session: session, action: 'calculate_voxels', **kwargs)
end
이 메서드에는 potree_state나 potree_url 검증이 없다. 검증은 별도의 check_calculate_voxels!에만 존재:
def check_calculate_voxels!
raise ... unless self.is_a?(::Capture) || self.is_a?(::Pointcloud)
return false if self.respond_to?(:pointcloud_group?) && self.pointcloud_group?
return false if self.has_attribute?(:pointcloud_type) && self.pointcloud_type == '3d_reconstructed'
raise Cupix::Errors::Parameter.new(code: 'ARG10060', reason: 'Potree is not uploaded') if self.respond_to?(:potree_state_uploaded?) && !self.potree_state_uploaded?
true
end
Rails의 potree_url 메서드는 revision이 0이면 nil을 반환:
def potree_url
return nil if revision.zero?
return self.sys[:origin_potree_url] if self.sys[:origin_potree_url].present?
Cupix::StorageService.object_url(storage_option: storage_option, key: potree_basepath(revision))
end
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-any-voxel-agent status:error "VoxelService"
service:cupixworks-any-voxel-agent "204434"
에러 로그 (Pointcloud ID 확인):
{
"timestamp": "2026-05-12T06:35:44.170Z",
"status": "error",
"message": "CPRealityCapture::validate | Invalid potreeUrl: for Pointcloud ID: 204434",
"meta": {
"team": { "domain": "endeavourgroup", "id": 180 },
"pointcloud": { "id": 204434 },
"user": { "id": 8624, "email": "chris.bellamy@auav.com.au" }
},
"region": "ap-southeast-2",
"host": "ip-10-1-46-225.ap-southeast-2.compute.internal"
}
동일 시간대 관련 에러:
2026-05-12T06:35:44.170Z - VoxelService::run | end - error: "Invalid potreeUrl"
2026-05-12T06:12:10.000Z - VoxelService::run | end - error: "ENOENT: no such file or directory, stat '/tmp/workspace/pointcloud/204400/raw/pointcloud_204400_voxels.csv'"
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Pointcloud 204434의 potree 파일이 아직 업로드되지 않은 상태에서 voxel 계산 요청이 전달됨 (potree_url이 null/empty) | 로그에서 Invalid potreeUrl: (빈 문자열) 확인. Rails potree_url 메서드는 revision.zero?이면 nil 반환 (potree.rb:15). calculate_voxels!에 potree_state 검증 없음 (voxel_service.rb:3-12) |
— | Confirmed |
| H2 | Agent가 API에서 pointcloud를 조회할 때 potree_url 필드가 serialization에서 누락됨 | SQS body에 pointcloud 데이터 일부만 포함 (voxel_agent.rb:28-32: fields는 id, name, voxel_state만 포함) |
Agent는 API를 통해 전체 pointcloud 데이터를 다시 조회함 (reality_capture.manager.ts:43-45), SQS 메시지의 부분 데이터를 사용하지 않음 |
Rejected |
| H3 | refresh_voxels!가 check_calculate_voxels! 검증을 우회하여 potree 미준비 상태의 pointcloud에 voxel 계산 요청 전달 |
refresh_voxels! (voxel_module.rb:81-82)는 개별 pointcloud에 대해 calculate_voxels!를 직접 호출, check_calculate_voxels!를 거치지 않음. Facility 레벨 호출 시에는 potree_unloaded scope 필터가 있으나 개별 pointcloud 직접 호출은 필터 없음 |
정확한 호출 경로 확인 불가 (worker 로그 미발견) | Confirmed (보조 원인) |
Fix Recommendation#
즉시 조치 (Critical)#
app/services/cupix/voxel_service.rb:3-12:calculate_voxels!메서드에 pointcloud인 경우potree_state_uploaded?검증을 추가해야 한다. 이 메서드가 SQS 메시지 전송 전 최종 게이트키퍼 역할을 해야 한다.
단기 개선 (1주 이내)#
- Voxel agent의
VoxelService::run(voxel-service.ts:63-66)에서 catch 블록의 에러 로그 메시지 형식을"end - error"대신 더 구체적인 형태로 개선하여, validation 실패(데이터 문제)와 실제 처리 에러를 구분할 수 있도록 한다. - Pointcloud 204434의 현재 상태를 확인하여 potree 업로드가 필요한지, 아니면 voxel 계산 대상에서 제외해야 하는지 판단한다.
장기 개선 (재발 방지)#
Cupix::VoxelService.calculate_voxels!에 precondition 검증을 통합하여, 모든 호출 경로(직접 호출,refresh_voxels!,check_calculate_voxels!)에서 동일한 검증 로직이 적용되도록 한다. 현재 검증 로직이 caller에 분산되어 있어 우회 가능성이 존재한다.
Monitoring#
- Voxel agent의 "Invalid potreeUrl" 에러 추적:
service:cupixworks-any-voxel-agent status:error "Invalid potreeUrl"
- Potree 미업로드 상태에서 voxel 계산 요청 감지 (Rails 측):
service:cupixworks-worker "ARG10060" "Potree is not uploaded"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial