ES /docs

RealityCaptureManager::loadRealityCapture | end - "HTTP request failed"

RCA: RealityCaptureManager::loadRealityCapture | end - "HTTP request failed"

Overview#

What Happened#

2026-07-13 18:12~18:13 KST (약 70초 구간)에 cupixworks-any-voxel-agent (ap-southeast-2, production) 여러 호스트에서 RealityCaptureManager::loadRealityCapture 가 32건 실패했다. 로그 메시지는 auto-generated tesla-v1 API 클라이언트가 non-2xx 응답에 대해 던지는 HttpError('HTTP request failed') 를 그대로 노출한 것으로, 실제 HTTP status/response body 는 로깅되지 않아 원인을 좁힐 수 없다. 이후 BaseService::cleanUpAnythingRelatedModel | end - undefined modelDirPath warn 이 이어져 각 메시지 처리가 초기 fetch 단계에서 중단된 정황을 보여준다.

Quick Facts#

Field Value
exception.class HttpError (tesla-v1 client)
exception.message HTTP request failed
top_frame packages/cupix-tesla-voxel-agent/src/manager/reality_capture.manager.ts:64
env production, region ap-southeast-2, tenant cupix

Affected Teams#

Team / Domain Error Count Impact
cupix (team.id 6, session f66c60830f9f...) 대부분 (관찰된 로그 다수) 다수 capture (예: 81780, 81782, 81783, 81786, 81796, 81814, 81824, 79457 등)의 voxel 생성이 실패하고 VoxelState.Error 로 마킹됨
endeavourgroup (team.id 180, session 84fd315b0927...) ≥1 (capture 81133 등) 같은 시간대 동일 warn 관찰. voxel 실패 개연성 있음 (uncertain — 동일 세션에서 HTTP request failed 로그가 발생했는지 별도 확인 필요)

Timeline#

  1. 2026-07-13 18:09:03 KSTCupix::VoxelService.calculate_voxels!error_code 이 이미 설정된 capture (81844, 81849, 81851, 81854, 81858, 81859, 81864, 81867, 81868) 에 대해 Cupix::Errors::Argument(ARG40000) "error state model cannot calculate voxel" 로 즉시 거절 (context; 이번 클러스터와는 다른 capture ID)
  2. 2026-07-13 18:12:22 KSTRealityCaptureManager::loadRealityCapture | end - "HTTP request failed" 최초 발생 (first_seen)
  3. 2026-07-13 18:13:24~32 KST — 동일 에러 다수 호스트/capture 에서 폭발적으로 반복 (32건, 약 70초)
  4. 2026-07-13 18:13:32~38 KST — 이어서 BaseService::cleanUpAnythingRelatedModel | end - undefined modelDirPath warn 이 각 capture 마다 뒤따름 (동일 세션 IDs 매칭)

Error Log#

Datadog Logs

text
RealityCaptureManager::loadRealityCapture | end - "HTTP request failed"

Impact#

  • Service: cupixworks-any-voxel-agent
  • Team: cupix (일부 endeavourgroup 세션도 관찰)
  • 발생 횟수: 32
  • 최초 발생: 2026-07-13 18:12 KST
  • 최근 발생: 2026-07-13 18:13 KST

Root Cause Summary#

RealityCaptureManager.loadRealityCapturecupixApi.capture.get(id) (혹은 pointcloud.get(id)) 호출로 tesla API 에 capture 를 조회한다. 이 호출은 auto-generated tesla-v1 client 를 통해 이루어지며, non-2xx 응답 시 HttpError(response, body, response.statusCode) 로 reject 된다. HttpError 는 부모 Error 의 message 에 항상 "HTTP request failed" 만 넣고, 실제 HTTP 상태 코드와 응답 body 는 별도 property (.statusCode, .body) 로만 보관한다. 그런데 agent 의 .catch 블록은 JSON.stringify(ec.message) 만 로깅하기 때문에, 실제 실패 원인(예: 401 인증 만료, 404 리소스 없음, 5xx 서버 오류, 네트워크 타임아웃 여부)이 로그에서 완전히 소실된다. 관찰된 32건의 에러는 원인 미상이 아니라 원인을 남기지 않도록 로깅된 실패이며, 클러스터의 근본 원인은 tesla-v1 client HttpError 를 다루는 에이전트 측 에러 로깅의 결함이다. 유력한 하위 원인(status code 미상)에 대해서는 아래 Hypotheses 표에서 별도 관리한다.

Technical Analysis#

Code Path#

  • Entry point: BaseService::runByMessageVoxelService::run (SQS 메시지 1건당 1회)
  • Failure point: RealityCaptureManager::loadRealityCapture.catch — reject 시 ec.message 만 로깅
  • Underlying error source: tesla-v1 auto-generated HttpError (message hard-coded 로 "HTTP request failed")
applications/agents/packages/cupix-tesla-voxel-agent/src/manager/reality_capture.manager.ts:39-67typescript
loadRealityCapture = (realityCaptureId: number, realityCaptureType: string): Promise<TESLA.Capture | TESLA.Pointcloud> => new Promise((resolve, reject) => {
    logger.debug('RealityCaptureManager::loadRealityCapture | begin, realityCaptureId: %d, realityCaptureType: %s', realityCaptureId, realityCaptureType);
    const getRealityCapture = (realityCaptureId: number): Promise<TESLA.Capture | TESLA.Pointcloud> => {
        if (realityCaptureType === 'capture') {
            return this.cupixApi.capture.get(realityCaptureId);
        } else if (realityCaptureType === 'pointcloud') {
            return this.cupixApi.pointcloud.get(realityCaptureId);
        } else {
            logger.error('RealityCaptureManager::loadRealityCapture | end - invalid reality capture type: %s', realityCaptureType);
            throw new Error(`Invalid reality capture type: ${realityCaptureType}`);
        }
    };

    getRealityCapture(realityCaptureId)
        .then(srvRealityCapture => {
            if (!srvRealityCapture || !srvRealityCapture.id || srvRealityCapture.id !== realityCaptureId) {
                logger.error('RealityCaptureManager::loadRealityCapture | end - failed to load reality capture');
                return reject();
            }
            this._realityCaptureId = realityCaptureId;
            this._realityCaptureType = realityCaptureType;
            logger.debug('RealityCaptureManager::loadRealityCapture | end - reality capture id: %d, type: %s', srvRealityCapture.id, realityCaptureType);
            resolve(srvRealityCapture);
        })
        .catch(ec => {
            logger.error('RealityCaptureManager::loadRealityCapture | end - %s', JSON.stringify(ec.message));
            reject(ec);
        });
});

ec 는 실제로는 HttpError 인데, ec.message 만 찍기 때문에 항상 "HTTP request failed" 라는 동일 문자열이 32건 반복된다. ec.statusCode, ec.body, ec.response?.headers (특히 x-request-id) 를 함께 로깅하지 않는 것이 결함.

cupix-api/client/openapi/typescript-node/tesla-v1/api/apis.ts:197-202typescript
export class HttpError extends Error {
    constructor (public response: http.IncomingMessage, public body: any, public statusCode?: number) {
        super('HTTP request failed');
        this.name = 'HttpError';
    }
}

호출 위치:

applications/agents/packages/cupix-tesla-voxel-agent/src/voxel-service.ts:45-67typescript
run = async (targetId: number, msgObject?: any): Promise<void> => {
    const targetType = msgObject.type ?? 'capture';
    logger.debug('VoxelService::run | begin - targetId: %d, targetType: %s', targetId, targetType);
    try {
        const serverRealityCapture = await this.realityCaptureManager.loadRealityCapture(targetId, targetType);
        const cpRealityCapture = this.realityCaptureManager.createCPRealityCapture(serverRealityCapture, 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);
    }
};

VoxelService::run 의 최상위 catcherror 를 객체로 넘기지만, 사용하는 logger wrapper 가 Error 를 어떤 필드로 직렬화하는지 별개 문제. 이 위치에도 status/body 가 명시적으로 표시되어야 한다.

기대 동작 vs 실제 동작:

  • 기대: capture.get 실패 시 로그에 HTTP status, response body, capture ID, request id 가 포함되어 원인 분석 가능.
  • 실제: "HTTP request failed" 만 반복. 32건이 같은 세션 f66c60830f9f... 에서 여러 호스트에 걸쳐 초당 여러 건씩 발생하는 정황상 특정 파라미터(예: capture ID)가 아닌 시스템-와이드 조건(인증 만료, tesla API 장애/재시작, 네트워크) 이 유력하나, 로그만으로는 결정 불가.

Log Evidence#

Datadog 쿼리 (클러스터에서 그대로 복사):

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

에러 로그 원문 (raw JSON 발췌):

json
{
  "message": "RealityCaptureManager::loadRealityCapture | end - \"HTTP request failed\"",
  "status": "error",
  "timestamp": "2026-07-13T09:13:32.390Z",
  "attributes": {
    "level": "error",
    "session": { "id": "f66c60830f9f328a00169df8fc54eb26cf9d9565" },
    "capture": { "id": 81824 },
    "team": { "domain": "cupix", "id": 6 },
    "environment": "production",
    "region": "ap-southeast-2",
    "user": { "id": 8, "email": "yohan.kim@cupix.com" },
    "host": { "name": "ip-10-1-168-201.ap-southeast-2.compute.internal" }
  }
}

follow-up warn (동일 트랜잭션 내):

json
{
  "message": "BaseService::cleanUpAnythingRelatedModel | end - undefined modelDirPath",
  "status": "warn",
  "timestamp": "2026-07-13T09:13:38.454Z",
  "attributes": {
    "session": { "id": "f66c60830f9f328a00169df8fc54eb26cf9d9565" },
    "capture": { "id": 81796 }
  }
}

주변 컨텍스트 (cupixworks-api 쪽 관련 있으나 별개 원인):

text
service:cupixworks-api status:error @2026-07-13T09:09:03Z~09:11Z
→ "failed to calculate voxels for Capture ID: 81868, error: error state model cannot calculate voxel"
   class=Capture function=calculate_voxels

이 API 에러들은 서로 다른 capture ID (81844, 81849, 81851, 81854, 81858, 81859, 81864, 81867, 81868) 에 대해 Cupix::VoxelService.calculate_voxels! 가 이미 error_code 가 있는 모델을 pre-flight 거절한 것이며, 이번 agent 클러스터의 capture ID 집합(81780, 81782, 81783, 81786, 81796, 81814, 81824, 79457, 81133 …) 과 겹치지 않아 직접적 원인은 아니다. 다만 동일 시간대에 voxel 파이프라인 부하가 몰렸다는 정황은 제공.

cupixworks-api 에서 09:11~09:14 UTC 구간에는 status:error 로그가 0건 검색되었다:

text
service:cupixworks-api status:error @2026-07-13T09:11:00Z~09:14:00Z
→ Found 0 logs

이는 (a) API 는 정상 응답했는데 client 측 처리에 문제가 있었거나, (b) API 가 실제로 응답했으나 성공 status (예: 401/404) 라서 API 자체는 error 로 로깅하지 않았거나, (c) 문제가 API 앞단(ALB/nginx/네트워크)에 있어 API 로그가 남지 않은 케이스 중 하나. 로그가 없으므로 확정 불가.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Agent 의 HttpError 처리 코드가 ec.message (하드코딩된 "HTTP request failed") 만 로깅해 실패 원인을 감춘다 (로깅 결함) tesla-v1 HttpError(super('HTTP request failed')) 정의 (cupix-api apis.ts:197-202) + agent .catch(ec => logger.error(..., JSON.stringify(ec.message))) (reality_capture.manager.ts:63-66) — 두 코드가 결합해 status/body 손실을 필연화 Confirmed (관찰된 로그가 100% "HTTP request failed" 단일 문자열인 점이 뒷받침)
H2 tesla API 가 5xx/timeout 을 반환해 실패 (인프라 장애) agent 32건 폭발, 여러 호스트/세션에 걸쳐 있음 → 시스템-와이드 조건 동시각 cupixworks-api status:error 0건 (09:11~09:14 UTC). ap-southeast-2 리전만 관측. status-board 에도 dep 인시던트 없음 (svc:cupixworks-any-voxel-agent::unknown) Inconclusive — 로그 부족으로 확정/기각 불가
H3 인증 세션 만료/무효로 401 이 반복 동일 세션 f66c60830f9f... 로 32건 집중 → 세션-스코프 조건 개연 세션 만료면 BaseService::authenticateByMessage 단계나 이후 다른 API 호출도 실패해야 하는데, loadEntityParameters 는 같은 시각 info 로그(entity parameters length: 4)가 남음 → 최소 일부 요청은 성공한 것으로 보임 (동일 세션이라도 서로 다른 message loop 이라 재확인 필요) Rejected (부분 성공 근거) — 단, uncertain, 확정하려면 debug/silly 로그 필요
H4 Upstream tesla API 가 pre-flight 에러(ARG40000) 로 이 capture 들을 거절 09:09~09:11 UTC 에 Cupix::VoxelService.calculate_voxels!ARG40000 로 다수 capture 거절 거절된 capture ID (81844, 81849, 81851, 81854, 81858, 81859, 81864, 81867, 81868) 와 agent 에러 capture ID (81780, 81782, 81783, 81786, 81796, 81814, 81824, 79457) 가 완전히 disjoint. 또한 calculate_voxels! 의 pre-flight 거절은 애초에 SQS 큐에 메시지를 넣지 않으므로 agent 까지 도달하지 않음 Rejected
H5 srvRealityCapture.id !== realityCaptureId mismatch 로 인한 reject (같은 함수의 다른 branch, reject() no-arg) 이 branch 는 .catch 에 들어가지 않고 위에서 별도 logger.error('...failed to load reality capture') 를 남김 관찰된 로그는 end - "HTTP request failed" 이지 end - failed to load reality capture 가 아님 → 이 branch 는 발생하지 않음 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: applications/agents/packages/cupix-tesla-voxel-agent/src/manager/reality_capture.manager.ts:63-66 접근: .catch(ec => ...) 블록에서 ec.message 뿐 아니라 ec.statusCode, ec.body (또는 ec.response?.statusCode, ec.response?.headers?.['x-request-id']) 를 함께 로깅하도록 확장. tesla-v1 auto-generated HttpError 는 message 를 하드코딩 ("HTTP request failed") 하므로 message 만 남기면 원인 소실이 필연이다. 방향만 지시 — 구현은 team 컨벤션(cupixApi 다른 매니저의 .catch 패턴)을 grep 해서 맞출 것.
  • 파일: applications/agents/packages/cupix-tesla-voxel-agent/src/voxel-service.ts:63-66 접근: 최상위 catch (error) 로그도 error.statusCode / error.body 를 명시적으로 포함하도록 보강 (현재 logger.error('...error', error) 만으로는 logger wrapper 에서 어떤 필드가 직렬화되는지 불투명).

단기 개선 (1주 이내)#

  • 소급 재조사 트리거: 현 클러스터의 실제 status code 를 확인할 수 있는 유일한 방법은 debug 로그 재생 또는 재발 시 로깅 개선 반영 후 재수집이다. 개선 반영 후 최소 1주 관찰하고, 유의 status code(5xx / 401) 가 나오면 별도 후속 RCA.
  • 에이전트 공통 유틸: cupix-tesla-*-agent 다수 매니저가 동일 tesla-v1 client 를 쓰므로, HttpError 를 정형 문자열(status={code} body={truncated})로 포맷팅해 로깅하는 헬퍼를 @agents/utils 에 추가하고 여러 매니저 catch 지점을 그것으로 대체.
  • 재시도 정책 검토: capture.get 은 참조 조회이므로 5xx/타임아웃에 대해 짧은 exponential backoff 재시도(최대 2회) 도입 여지 검토. 4xx 는 재시도 대상 아님.

장기 개선 (재발 방지)#

  • tesla-v1 auto-generated client 를 커스터마이즈하거나 wrapper 를 두어 HttpError.message 에 status/reason phrase 를 포함시키는 방향 검토 (cupix-api 의 openapi 템플릿 수정 필요). 모든 agent 를 개별 수정하는 비용을 낮춘다.
  • RealityCaptureManager::loadRealityCapture 에서 no-arg reject() (line 56) 는 downstream 에서 undefined 로 catch 되어 로깅이 더 열악해진다 — 명시적 Error 객체로 reject 하도록 통일 (사후 관찰용, 이번 이슈와 별개 개선점).

Monitoring#

text
service:cupixworks-any-voxel-agent status:error "RealityCaptureManager::loadRealityCapture"
text
service:cupixworks-any-voxel-agent status:error "HTTP request failed"
text
service:cupixworks-any-voxel-agent status:error @region:ap-southeast-2

로깅 개선 후에는 status code (@statusCode:5*, @statusCode:401) 등으로 분해된 쿼리를 추가할 것.

Risk Assessment#

  • Risk level: medium (32건이 70초 안에 다수 capture 에 걸쳐 발생 → 여러 팀 사용자의 voxel 생성이 실패한 사용자-영향 이슈. 다만 재발/지속성은 이 시점 데이터로는 확정 불가)
  • 예상 복잡도: trivial (catch 블록에서 로깅 필드 추가 — 1-3줄 변경). 단, 소급 원인 확정은 재발 대기 필요.