PotreeService::handlingMessageErrors | Errors in error handling - {"response":{"statusCode":403,"bod
RCA: PotreeService::handlingMessageErrors | Errors in error handling
Overview#
What Happened#
2026-06-04 02:15 KST에 cupixvista-any-potree-agent 서비스에서 pointcloud ID 28736에 대한 potree 변환 처리 중 에러 핸들링 과정에서 이차 에러가 발생했다. 초기 에러(undefined response) 발생 후 에러 핸들러가 pointcloud 상태를 Error로 업데이트하려 했으나, 해당 pointcloud가 이미 삭제되어 Tesla API가 403(Not Found) 응답을 반환했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | HttpError |
| exception.message | Pointcloud not found (ARG10002) |
| top_frame | potree-service.ts:519 |
| env | production, us-west-2 |
Timeline#
- 2026-06-04 02:14:59 KST — potree-agent가 SQS 메시지 수신, pointcloud 28736 처리 시작
- 2026-06-04 02:15:00 KST — PotreeConverter 실행
- 2026-06-04 02:15:10 KST — 처리 중 에러 발생,
handlingMessageErrors진입 - 2026-06-04 02:15:10 KST —
updatePotreeState호출 시 403 에러 (Pointcloud not found) - 2026-06-04 02:15:10 KST — SQS 메시지 삭제 완료
Error Log#
PotreeService::handlingMessageErrors | Errors in error handling - {"response":{"statusCode":403,"body":{"result":{"code":"ARG10002","type":"Cupix::Errors::NotFound","reason":"Pointcloud not found","message":"Pointcloud not found"}},...,"request":{"uri":{"pathname":"/api/v1/pointclouds/28736"},"method":"PUT",...}},"statusCode":403,"name":"HttpError"}
Impact#
- Service:
cupixvista-any-potree-agent - Team: mtu-team
- 발생 횟수: 1
- 최초 발생: 2026-06-04 02:15 KST
- 최근 발생: 2026-06-04 02:15 KST
Root Cause Summary#
Potree 변환 처리 중 초기 에러가 발생하여 handlingMessageErrors가 호출되었다. 이 핸들러는 updatePotreeState를 통해 pointcloud 상태를 Error로 업데이트하려 했으나, 대상 pointcloud(ID: 28736)가 이미 Tesla API에서 삭제된 상태였다. Tesla는 Cupix::Errors::NotFound를 HTTP 403으로 매핑하므로 403 응답이 반환되었고, 이 이차 에러가 catch 블록(line 518-519)에서 로깅된 것이 본 클러스터의 에러이다. 근본 원인은 에러 핸들러가 pointcloud 존재 여부를 확인하지 않고 상태 업데이트를 시도하는 구조적 문제이다.
Technical Analysis#
Code Path#
- Entry point:
potree-service.ts:54(checkingQueue) - 메시지 처리:
potree-service.ts:130-136(runByMessages→runByMessage) - 에러 포착:
potree-service.ts:76-78—runByMessages()에서 throw된 에러를 catch - 에러 핸들러:
potree-service.ts:502-525(handlingMessageErrors) - Failure point:
potree-service.ts:517(updatePotreeState)
에러 핸들링 흐름:
try {
await this.runByMessages();
} catch (error) {
await this.handlingMessageErrors(error);
}
초기 에러(PotreeConverter 실패 등)가 발생하면 handlingMessageErrors로 진입한다.
const apiErrorObject = this.getApiErrorToDeleteMessage(error);
if (apiErrorObject != undefined || this.checkReceiveCountToDeleteMessage()) {
try {
errorAndMessage.error = apiErrorObject;
await this.deleteByMessage(this.messageInProcess);
if (this._modelInProcess != undefined) await this.updatePotreeState(this._modelInProcess.id, TESLA.PointcloudPotreeState.Error);
} catch (error) {
logger.error('PotreeService::handlingMessageErrors | Errors in error handling - %s', JSON.stringify(error));
}
}
getApiErrorToDeleteMessage에서 초기 에러의 .response가 undefined이면 'undefined response' 문자열을 반환한다:
const response = CPUtils.isJsonString(error) ? JSON.parse(error) : error.response;
if (response == undefined) {
logger.warn('PotreeService::getApiErrorToDeleteMessage | undefined response - %s', JSON.stringify(error));
return 'undefined response';
}
이후 updatePotreeState가 PUT /api/v1/pointclouds/28736을 호출하지만, Tesla API의 before_action :set_pointcloud에서 해당 레코드를 찾지 못해 NotFound → 403을 반환한다:
if model.nil?
if self.where(attrs).in_trash.present?
raise Cupix::Errors::NotFound.new(code: 'ENT4000', reason: "#{current_class.name} not found")
else
raise Cupix::Errors::NotFound.new(code: 'ARG10002', reason: "#{current_class.name} not found")
end
end
rescue_from Cupix::Errors::NotFound, with: :not_found_403_error
def not_found_403_error(exception)
raise_error(403, exception)
end
기대 동작: 에러 핸들러가 pointcloud 존재 여부를 확인하거나 updatePotreeState 실패를 무시해야 함.
실제 동작: pointcloud가 삭제된 상태에서 updatePotreeState 호출 시 403 에러가 발생하고, 이를 별도 try-catch로 잡아 error 레벨로 로깅.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixvista-any-potree-agent status:error "PotreeService::handlingMessageErrors"
Time: 2026-06-03T16:00:00Z to 2026-06-03T18:00:00Z
service:cupixvista-any-potree-agent
Time: 2026-06-03T16:00:00Z to 2026-06-03T18:00:00Z
핵심 로그 시퀀스 (KST):
{"timestamp": "2026-06-04 02:14:59", "status": "info", "message": "PotreeService::runByMessage | id: 28736"}
{"timestamp": "2026-06-04 02:14:59", "status": "info", "message": "PotreeService::runByMessage | state: queued, resource_state: uploaded, potree_state: created"}
{"timestamp": "2026-06-04 02:15:00", "status": "info", "message": "PotreeService::runPotreeConvertor | exec - /tmp/PotreeConverter/build/PotreeConverter ./potree_converter_input.json"}
{"timestamp": "2026-06-04 02:15:10", "status": "warn", "message": "PotreeService::getApiErrorToDeleteMessage | undefined response - {}"}
{"timestamp": "2026-06-04 02:15:10", "status": "error", "message": "PotreeService::handlingMessageErrors | Errors in error handling - {\"response\":{\"statusCode\":403,\"body\":{\"result\":{\"code\":\"ARG10002\",...\"reason\":\"Pointcloud not found\"}}},\"request\":{\"uri\":{\"pathname\":\"/api/v1/pointclouds/28736\"},\"method\":\"PUT\"}}"}
{"timestamp": "2026-06-04 02:15:10", "status": "error", "message": "PotreeService::handlingMessageErrors | Error and message object - {\"error\":\"undefined response\",\"sqsMessage\":{\"MessageId\":\"0e4026b6-ead6-41db-b7a2-fdc5bdcefcd3\",\"Attributes\":{\"ApproximateReceiveCount\":\"1\"}}}"}
시퀀스 분석:
- 처리 시작 시(02:14:59) pointcloud 28736은 존재했음 (
state: queued) - 약 11초 후(02:15:10) 에러 핸들링 시점에 pointcloud가 이미 삭제됨
- 초기 에러의
.response가 undefined였으므로 PotreeConverter 자체 실패(비 HTTP 에러)로 추정
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 에러 핸들러에서 updatePotreeState 호출 시 pointcloud가 이미 삭제되어 403 발생 |
로그에서 PUT /api/v1/pointclouds/28736이 403 반환, Tesla 코드에서 NotFound → 403 매핑 확인 (client_error_controller.rb:27), base_repository.rb:355에서 ARG10002 raise |
— | Confirmed |
| H2 | 인증 토큰 만료로 인한 403 | HTTP 403 응답 | 에러 body에 ARG10002/Pointcloud not found 명시, 인증 에러면 401 반환됨 (client_error_controller.rb:23-24) |
Rejected |
| H3 | 권한(permission) 부족으로 인한 403 | Tesla가 permission denied도 403으로 반환 (client_error_controller.rb:26) |
Permission denied는 code PERM10000으로 반환됨 (base_repository.rb:360), 실제 code는 ARG10002 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 파일:
packages/cupix-tesla-potree-agent/src/potree-service.ts:517 updatePotreeState호출 전에 pointcloud 존재 여부를 확인하거나, 해당 호출 실패 시 에러를 무시(warn 레벨로 로깅)하도록 변경. 에러 핸들러 내부에서 발생하는 이차 에러는 시스템에 치명적이지 않으므로, 403/404 응답 시 warn으로 로깅하고 진행하는 것이 적절함.
단기 개선 (1주 이내)#
handlingMessageErrors내updatePotreeState호출을 별도 try-catch로 감싸되, 현재 구조에서는 이미 try-catch가 있으므로 catch 블록의 로그 레벨을error→warn으로 변경. 삭제된 리소스에 대한 상태 업데이트 실패는 정상적 운영 시나리오이므로 error가 아닌 warn이 적절.
장기 개선 (재발 방지)#
- SQS 메시지 처리 시 pointcloud 존재 여부를 사전 검증하는 패턴 도입:
createCPPointcloudByPointcloudId(line 238-256)처럼 ARG10002에 대한 graceful handling을updatePotreeState에도 적용. - 전체 agent 서비스에서 에러 핸들러 내 API 호출 실패를 일관되게 warn 레벨로 처리하는 공통 유틸리티 도입 검토.
Monitoring#
updatePotreeState실패 빈도 모니터링 쿼리:
service:cupixvista-any-potree-agent "Errors in error handling" status:error
- 로그 레벨 변경 후 warn 빈도 추적:
service:cupixvista-any-potree-agent "Errors in error handling" status:warn
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial