failed to calculate voxels for Capture ID: 745783, error: error state model cannot calculate voxel
RCA: failed to calculate voxels for Capture ID: 745783, error: error state model cannot calculate voxel
Overview#
What Happened#
2026-08-01 02:25:36 KST 에 cupixworks-worker (production, us-west-2) 에서 Capture ID 745783 의 voxel 계산이 실패했다. Capture 가 이미 error_code=TES10002 상태였는데, editing 이 skipped 로 종료되는 finalization 콜백이 calculate_voxels 를 호출했고, Cupix::VoxelService.calculate_voxels! 가 error_code.present? 검증(voxel_service.rb:7) 에 걸려 Cupix::Errors::Argument("error state model cannot calculate voxel") 를 raise → VoxelModule#calculate_voxels 의 rescue 가 이를 error 레벨로 로깅했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Cupix::Errors::Argument (code ARG40000) |
| exception.message | error state model cannot calculate voxel |
| top_frame | app/services/cupix/voxel_service.rb:7 (raise), app/models/concerns/voxel_module.rb:12 (log) |
| env | production, us-west-2 |
| tenant | cupix |
| affected model | Capture id=745783, error_code=TES10002 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| capture / editing finalization (cupixworks-worker) | 1 (이 클러스터) / 14일 누적 5건 | 사용자 기능 영향 없음 — 이미 에러 상태인 Capture 에 대한 voxel 재계산 시도만 실패. 노이즈 에러 로그 발생. |
Timeline#
- 2026-08-01 02:25:36 KST — Capture 745783 finalization 진입:
start validation for 3D reconstruction. capture_id: 745783, editing_support: true(info) - 2026-08-01 02:25:36 KST — 동일 시각,
error_code=TES10002로 인해publish,3d_reconstruction,analysis,CaptureIntelligence,editing모두 정상적으로 skip 됨 (capture 745783 publish skipped: error_code(TES10002) exist,Editing skipped on Capture 745783등 info 로그) - 2026-08-01 02:25:36 KST —
after_editing_finalized :calculate_voxels콜백 실행 →Cupix::VoxelService.calculate_voxels!가error_code.present?검증에서ARG40000raise →VoxelModule#calculate_voxelsrescue 에서 error 로그 발생 (본 클러스터) - 2026-08-01 02:26:08 KST —
Updating associated deviations for Capture 745783(info) — 후속 처리는 정상 진행 - 2026-08-01 02:26:10 KST —
Updating associated sitetracks for Capture 745783(info) — 후속 처리는 정상 진행
Error Log#
failed to calculate voxels for Capture ID: 745783, error: error state model cannot calculate voxel
Impact#
- Service:
cupixworks-worker - 발생 횟수: 1 (이 클러스터), 14일 누적 동일 fingerprint 5건 (Capture 78040, 78123, 78180, 738079, 745783)
- 최초 발생: 2026-08-01 02:25:36 KST
- 최근 발생: 2026-08-01 02:25:36 KST
- 사용자 영향: 없음. 이미 에러 상태 (
error_codeset) 인 Capture 에 대한 voxel 재계산 시도가 실패한 것으로, 정상적인 방어 로직이 작동한 결과이며 후속 처리(deviations, sitetracks 업데이트)는 계속 진행된다. 다만 정상 흐름에서error레벨 로그가 발생하여 알림 노이즈가 된다.
Root Cause Summary#
Capture#editing_state 가 skipped (또는 done) 으로 전이될 때 등록된 finalization 콜백 체인이 error_code 유무와 무관하게 calculate_voxels 를 호출한다. after_editing_finalized :calculate_voxels (app/models/concerns/quality_assurancable/editing_element.rb:21) 는 사전에 error 상태를 확인하지 않고, VoxelModule#check_calculate_voxels! (app/models/concerns/voxel_module.rb:35-44) 도 pointcloud_type / potree_state 만 검사할 뿐 error_code 는 검사하지 않는다. 결국 Cupix::VoxelService.calculate_voxels! (app/services/cupix/voxel_service.rb:7) 의 guard clause 에서 Cupix::Errors::Argument(ARG40000) 이 raise 되고, VoxelModule#calculate_voxels (app/models/concerns/voxel_module.rb:9-17) 의 rescue => e 가 이를 Cupix::Logger.error 로 기록한다. 즉 다른 finalization 콜백들 (publish_on_finish?, run_3d_reconstruction?, run_analysis?, run_capture_intelligence?) 은 모두 error_code 를 사전 확인하여 info 레벨 skip 로그만 남기지만, calculate_voxels 만 사전 확인 없이 서비스 레이어까지 내려간 뒤 예외로 튕겨나오는 비대칭 구조가 원인이다.
Technical Analysis#
Code Path#
Entry point: app/models/concerns/quality_assurancable/editing_element.rb:171-173 — editing_state 가 skipped/done 으로 전이되면 run_editing_finalized_callback 을 실행.
after_transition from: any, to: %i[done], &:edit_done_handler
after_transition from: any, to: %i[skipped done] do |model, transition|
model.run_editing_finalized_callback
end
콜백 등록: app/models/concerns/quality_assurancable/editing_element.rb:21 — after_editing_finalized :calculate_voxels.
included do
include ::QualityAssurancable::Editing
include ::QualityAssurancable::EditingElement::Callbacks
after_trash :fix_invalid_capture_editing_state
after_untrash :fix_invalid_capture_editing_state
after_editing_finalized :calculate_voxels
VoxelModule#calculate_voxels 는 error_code 를 사전 확인하지 않고 그대로 calculate_voxels! 를 호출하며, 예외를 rescue 하여 error 레벨로 기록한다.
def calculate_voxels(session: nil)
calculate_voxels!(session: session)
rescue => e
Cupix::Logger.error("failed to calculate voxels for #{self.class.name} ID: #{id}, error: #{e.message}", class: self.class.name, function: __method__, model: { id: id, type: self.class.name })
false
else
true
end
# ...
def calculate_voxels!(session: nil)
if check_calculate_voxels!
Cupix::VoxelService.calculate_voxels!(model: self, session: session)
end
end
def check_calculate_voxels!
raise Cupix::Errors::Parameter.new(code: 'ARG10000', reason: 'model must be a capture or pointcloud') 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
Failure point: app/services/cupix/voxel_service.rb:7 — service 진입부 guard clause 에서 raise.
def calculate_voxels!(model: nil, session: nil, **kwargs)
return unless Cupix::Tesla.launch_mode == 'CUPIXWORKS'
raise Cupix::Errors::Argument.new(code: 'ARG10000', reason: 'model is required') if model.blank?
raise Cupix::Errors::Argument.new(code: 'ARG10000', reason: 'model must be a capture or pointcloud') unless model.is_a?(::Capture) || model.is_a?(::Pointcloud)
raise Cupix::Errors::Argument.new(code: 'ARG40000', reason: 'error state model cannot calculate voxel') 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
기대 동작 vs 실제 동작
- 기대: 다른 finalization/publish 콜백들처럼
error_code가 있으면 사전에 check 하고 info 레벨로 skip 로그만 남긴다 (예:capture 745783 publish skipped: error_code(TES10002) exist). - 실제:
calculate_voxels만 사전 체크가 없어 서비스 레이어까지 진입 후 raise → rescue →error레벨 로그.
Log Evidence#
Datadog 쿼리 (본 클러스터):
service:cupixworks-worker status:error @environment:production "failed to calculate voxels for Capture ID: 745783, error: error state model cannot calculate voxel"
Datadog 쿼리 (14일 누적, 동일 fingerprint):
service:cupixworks-worker status:error "error state model cannot calculate voxel"
Capture 745783 주변 (24h) 로그 스니펫 — error_code=TES10002 상태에서 다른 콜백은 모두 skip, calculate_voxels 만 error 로 로그됨:
service:cupixworks-worker "745783"
{"timestamp":"2026-08-01 02:25:36","status":"info","message":"start validation for 3D reconstruction. capture_id: 745783, editing_support: true","class":"Capture","function":"run_3d_reconstruction?"}
{"timestamp":"2026-08-01 02:25:36","status":"info","message":"3D reconstruction was skipped due to the existence of an capture error code. capture_id: 745783, error_code: TES10002","function":"run_3d_reconstruction?"}
{"timestamp":"2026-08-01 02:25:36","status":"info","message":"Analysis skipped: error_code=TES10002. capture_id: 745783","function":"run_analysis?"}
{"timestamp":"2026-08-01 02:25:36","status":"info","message":"CaptureIntelligence skipped: error_code=TES10002. capture_id: 745783","function":"run_capture_intelligence?"}
{"timestamp":"2026-08-01 02:25:36","status":"info","message":"capture 745783 publish skipped: error_code(TES10002) exist","function":"publish_on_finish?"}
{"timestamp":"2026-08-01 02:25:36","status":"info","message":"Editing skipped on Capture 745783","class":"Capture","function":"start_finalization"}
{"timestamp":"2026-08-01 02:25:36","status":"error","message":"failed to calculate voxels for Capture ID: 745783, error: error state model cannot calculate voxel","class":"Capture","function":"calculate_voxels"}
14일 재현 이력 — 동일 exception, 서로 다른 Capture ID:
2026-07-18 16:15:35 — Capture ID 78040
2026-07-19 12:17:40 — Capture ID 738079
2026-07-19 16:05:36 — Capture ID 78123
2026-07-20 16:15:37 — Capture ID 78180
2026-08-01 02:25:36 — Capture ID 745783
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Editing finalization 콜백이 error_code 사전 검증 없이 calculate_voxels 를 호출하여, Cupix::VoxelService.calculate_voxels! 의 guard clause 에서 raise 된 예외가 error 레벨로 로깅됨 |
editing_element.rb:21 의 after_editing_finalized :calculate_voxels 무조건 등록, voxel_module.rb:35-44 의 check_calculate_voxels! 가 error_code 미검사, voxel_service.rb:7 의 guard 가 exact 메시지 "error state model cannot calculate voxel" 로 raise. 동일 시각 Capture 745783 의 다른 finalization 콜백(publish_on_finish?, run_3d_reconstruction? 등) 은 error_code=TES10002 를 이유로 skip 로그 남김 |
— | Confirmed |
| H2 | 외부 voxel-service (data-pipeline-functions) 장애로 인한 timeout/5xx | 예외 메시지가 코드에 하드코딩된 문자열과 정확히 일치 — 네트워크 예외 메시지와 다름. voxel-service 호출은 guard clause 이후에만 발생하며, 이 케이스는 guard 에서 raise 됨 | 예외 메시지 "error state model cannot calculate voxel" 은 voxel_service.rb:7 의 리터럴이며 RestClient/JSON 예외 메시지 포맷과 상이 |
Rejected |
| H3 | Capture 745783 이 삭제되었거나 model 이 blank | 로그에 Capture ID 가 명시되고, 후속 콜백(Updating associated deviations/sitetracks) 이 정상 실행됨. blank 였다면 ARG10000 model is required 로 raise 되었을 것 |
실제 메시지는 ARG40000 (error state model cannot calculate voxel) 이며 이는 model.error_code.present? 경로 |
Rejected |
| H4 | 크론(Cupix::Cron::Capture.recalculate_voxels) 이 error 상태 Capture 를 다시 스캔해서 재시도 |
크론 진입 로그가 이 시각에 없음. 로그 흐름은 finalization 콜백 체인 (start_finalization, run_3d_reconstruction?, publish_on_finish?) 순서로 이어져 editing_state 전이에서 발생 |
크론 시각(config/schedule.rb:76) 과 로그 시각 불일치, 크론 실행 로그 없음 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
없음 — 사용자 기능 영향 없이 error 로그만 발생하는 낮은 심각도. 후속 처리도 정상 진행됨.
단기 개선 (1주 이내)#
app/models/concerns/voxel_module.rb:35-44check_calculate_voxels!에 error_code 조기 반환 추가has_attribute?(:error_code) && error_code.present?이면return false하여 서비스 레이어 진입 전에 skip. 다른 finalization 콜백과 동일한 방어 패턴을 갖추게 된다.- 또는
app/models/concerns/voxel_module.rb:9의 wrappercalculate_voxels에서 error_code 체크 후 info 레벨 skip 로그 남기고 조기 return. - 근거: publish/3d_reconstruction/analysis/capture_intelligence 콜백은 이미
error_code를 사전 검사해 info 레벨 "skipped" 로그만 남긴다(로그 증거 참조). 동일 패턴을 voxel 콜백에도 적용하면 error 레벨 노이즈가 사라진다.
- 대안: rescue 절 로그 레벨 조정
voxel_module.rb:11-14의 rescue 에서 예외가Cupix::Errors::Argument(특히 codeARG40000) 인 경우warn또는info로 다운그레이드. 단, memory rule ("Scope warn-level downgrades to the specific exception class") 에 따라rescue => e를 통째로 warn 화하지 말고, 해당 예외 클래스/코드에 한정할 것.- 사전 체크 (
check_calculate_voxels!수정) 이 더 근본적인 해결이므로 우선 순위.
장기 개선 (재발 방지)#
- Finalization 콜백 등록 시 공통 guard (error_code 검사) 를 강제하는 상위 helper (예:
after_editing_finalized_when_no_error_code :calculate_voxels) 도입 검토. 향후 새로운 finalization 콜백이 추가될 때 동일한 비대칭 문제가 재발하지 않도록 보장. - error_code 상태 Capture 에 대한 콜백 실행 정책(모두 skip vs 일부 실행) 을 팀 내 문서화. 현재는 콜백별로 개별 판단이라 일관성이 없다.
Monitoring#
- 에러 트렌드 확인 (동일 fingerprint 재발 감지):
service:cupixworks-worker status:error "error state model cannot calculate voxel"
- error_code 있는 Capture 의 finalization 이후 error 로그 비율:
service:cupixworks-worker status:error @class:Capture @function:calculate_voxels
- 정상 skip 패턴(비교용, info 레벨):
service:cupixworks-worker status:info "publish skipped: error_code"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (voxel_module 의 wrapper 또는 check_calculate_voxels! 에 error_code 조기 반환 한 줄 추가 + 사이드 이펙트가 다른 흐름을 막지 않도록 spec 보강)