HTTP request failed
RCA: HTTP request failed
Overview#
What Happened#
2026-06-30 07:36 KST 에 cupixworks-any-room-agent 가 BIM 20436 에 대한 room 추출 작업을 처리하는 도중, 사용자가 동시에 해당 BIM 을 trash 시켜 후속 API 호출(GET /api/v1/rooms?bim_id=20436) 이 403 ENT4000 "Bim not found" 로 실패했다. tesla-v1 OpenAPI 클라이언트가 4xx 응답에 대해 던지는 HttpError("HTTP request failed") 가 그대로 SQS 메시지 핸들러로 전파되어 error 로그가 1건 기록되었다. SQS 메시지는 4xx 정책에 따라 재시도 없이 삭제되었으므로 사용자 영향은 없다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | HttpError |
| exception.message | HTTP request failed |
| top_frame | cupix-api/client/openapi/typescript-node/tesla-v1/api/apis.ts:199 |
| underlying.status | 403 |
| underlying.body | {"code":"ENT4000","type":"Cupix::Errors::NotFound","reason":"Bim not found"} |
| request | GET http://api-tesla.cupix.internal/api/v1/rooms?bim_id=20436&... |
| modelId | 20436 |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| ctsarquitetura (room extraction) | 1 | 사용자 영향 없음 — BIM 이 사용자 본인의 의도로 trash 처리되었고 SQS 메시지는 4xx 정책으로 즉시 삭제됨 |
Timeline#
- 2026-06-30 07:35:57 KST —
cupixworks-any-room-agent가 SQS 메시지 수신,BaseService::runByMessage | id: 20436시작 - 2026-06-30 07:35:58 KST —
[200] GET /api/v1/bims/20436(agent 가 BIM 메타 조회 성공) - 2026-06-30 07:36:00 ~ 07:36:17 KST — agent 가 Forge SVF2 room 추출 수행, 그 사이 사용자가 BIM 편집(
PUT /api/v1/bims/20436다수) - 2026-06-30 07:36:17 KST — 사용자가
PUT /api/v1/bims/20436/trash호출,[204]응답 (BIM 이 trashed 상태로 전환) - 2026-06-30 07:36:22 KST — agent 가 추출 결과를 서버와 동기화하려고
GET /api/v1/rooms?bim_id=20436호출 →403 ENT4000 Bim not found,HttpError("HTTP request failed")발생 - 2026-06-30 07:36:22 KST —
BaseService::handlingMessageErrors가 에러를 기록 후 SQS 메시지 삭제 (4xx 정책) - 2026-06-30 07:36:24 KST — agent 의 후속
PUT /api/v1/bims/20436(room_state 업데이트 시도) 도403으로 실패하고 종료
Error Log#
HTTP request failed
Impact#
- Service:
cupixworks-any-room-agent - Team: ctsarquitetura
- 발생 횟수: 1
- 최초 발생: 2026-06-30 07:36 KST
- 최근 발생: 2026-06-30 07:36 KST
사용자 영향은 없다. trash 행위 자체가 사용자의 명시적 의도이며, 진행 중이던 room 추출 결과는 더 이상 의미가 없다. SQS 메시지는 4xx 응답에 대해 즉시 삭제되므로 재시도 폭주도 없다.
Root Cause Summary#
Room 추출 작업이 진행되는 약 25초의 윈도우 동안 사용자가 동일한 BIM(id=20436) 을 trash 시켰다. tesla API 의 BimRepository.show 는 기본적으로 Cyclable.visibility[:UNTRASHED] 스코프로 동작하므로, trashed BIM 에 대한 후속 GET /api/v1/rooms?bim_id=20436 호출이 Cupix::Errors::NotFound (ENT4000) 을 반환했다. 이 응답은 tesla-v1 OpenAPI 클라이언트(apis.ts:199) 에서 HttpError("HTTP request failed") 로 래핑되어 던져졌고, 호출부(RoomService::importRoomsFromBim → cupixApi.room.getAll) 에서 잡히지 않은 채 BaseService::handlingMessageErrors 까지 전파되어 error 로그로 남았다. 본질적으로 모델의 lifecycle race condition 이며 코드 버그가 아니라 운영상 정상 시나리오에 가깝지만, 현재는 error 레벨로 로깅되어 alert noise 가 된다.
Technical Analysis#
Code Path#
Entry point — agent SQS message loop:
try {
await this.runByMessages();
} catch (error) {
await this.handlingMessageErrors(error);
}
this.resetMessages();
await CPUtils.sleep(500);
await this.checkingQueue();
Room agent 메인 시퀀스 — 추출 완료 후 서버 rooms 와 diff 하기 위해 room.getAll(bim_id) 호출:
const serverRooms = await this.cupixApi.room.getAll(cpBim.id);
logger.info('RoomService::importRoomsFromBim | server room size: %d', serverRooms.length);
room.getAll 은 tesla-v1 OpenAPI 클라이언트의 getRooms 를 호출 (bim_id 필터):
async getAll(bimId: number, listParams?: ListParams): Promise<TESLA.Room[]> {
const api = await this.api();
return paginateAll<TESLA.Room>(async (page, perPage) => {
const res = await api.getRooms(
Fields.RoomFields,
bimId,
undefined,
listParams?.X_CUPIX_UPDATED_SINCE,
listParams?.order_by,
page,
perPage,
"HTTP request failed" 문자열의 정확한 발화 지점 — tesla-v1 OpenAPI 자동 생성 클라이언트:
export class HttpError extends Error {
constructor (public response: http.IncomingMessage, public body: any, public statusCode?: number) {
super('HTTP request failed');
this.name = 'HttpError';
}
}
서버측 — 트래쉬된 BIM 은 default scope 에서 보이지 않음. RoomsController#index 는 RoomRepository#search 를 통해 BimRepository#show(bim_id) 를 호출하고, 이 시점에 Cupix::Errors::NotFound (ENT4000) 가 발생:
if self.query_option.bim_id.present? || self.query_option.bim_revision_id.present?
if self.query_option.bim_id.present?
bim = BimRepository.new(current_user: self.current_user).show(self.query_option.bim_id, visibility: visibility, review_id: review_id)
self.query_option.query[:bool][:must] << {
term: {
"bim.id": bim.id
}
}
에러 핸들링 — 4xx 응답은 SQS 메시지를 삭제하고 한번 error 로그를 남김:
private getApiErrorToDeleteMessage = (error: any): any => {
if (error == undefined) { ... }
if (error.errno != undefined && error.code != undefined && error.syscall != undefined) { ... }
const response = CPUtils.isJsonString(error) ? JSON.parse(error) : error.response;
if (response == undefined) { ... }
const statusCode = response.statusCode ? Number(response.statusCode) : undefined;
...
if (statusCode != undefined && statusCode >= 400 && statusCode <= 500) {
if (statusCode === 401) return;
return errorMsg;
}
return;
};
private handlingMessageErrors = async (error: any): Promise<void> => {
...
const apiErrorObject = this.getApiErrorToDeleteMessage(error);
if (apiErrorObject != undefined || this.checkReceiveCountToDeleteMessage()) {
try {
errorAndMessage.error = apiErrorObject;
await this.deleteByMessage(this.messageInProcess);
if (this._modelInProcess != undefined && this._modelInProcess.id > 0) await this.updateErrorState(this._modelInProcess);
} catch (error) { ... }
}
logger.error('BaseService::handlingMessageErrors | Error and message object - %s', JSON.stringify(errorAndMessage));
resetLogMeta();
};
기대 동작: 처리 도중 BIM 이 trash 되면, 후속 동기화 호출은 "이 작업은 이미 무의미함" 으로 인지하고 warn 레벨에서 조용히 종료되어야 한다.
실제 동작: 4xx 가 error 로그를 발생시키고, updateErrorState 가 호출되어 trash 직전의 BIM 상태(이미 cyclable scope 밖) 에 room_state=error 를 쓰려다 또 다시 403 으로 실패한다 (07:36:24Z PUT 로그 확인).
Log Evidence#
Datadog 쿼리:
service:cupixworks-any-room-agent 20436 status:(info OR warn OR error)
Agent 측 시퀀스 (UTC):
2026-06-29T22:35:57.669Z info BaseService::runByMessage | id: 20436
2026-06-29T22:36:22.692Z warn BaseService::getApiErrorToDeleteMessage | error msg - statusCode:403 ...bim_id=20436... ENT4000 "Bim not found"
2026-06-29T22:36:22.697Z warn CupixAuth::handleError | Response statusCode: 403 ... ENT4000 "Bim not found"
2026-06-29T22:36:22.799Z error HTTP request failed
2026-06-29T22:36:22.800Z info BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/20436
핵심 에러 페이로드:
{
"statusCode": 403,
"requestUriHref": "http://api-tesla.cupix.internal/api/v1/rooms?fields%5B0%5D=id&...&bim_id=20436&page=1&per_page=100",
"bodyResult": {
"code": "ENT4000",
"type": "Cupix::Errors::NotFound",
"reason": "Bim not found",
"message": "Bim not found"
},
"modelId": 20436,
"sqsMessage": { "MessageId": "c2103e11-89e1-41a3-81db-d149f10740cc", "Attributes": { "ApproximateReceiveCount": "1" } }
}
서버 측에서 본 동일 시점의 BIM 20436 lifecycle:
2026-06-30 07:32:14 KST info Cachable::ReviewLoad | Invalidated facility review cache on create | model=Bim | model_id=20436 | facility_id=21390
2026-06-30 07:35:44 KST info [200] GET /api/v1/bims/20436
2026-06-30 07:35:52 KST info [200] PUT /api/v1/bims/20436
2026-06-30 07:35:58 KST info [200] GET /api/v1/bims/20436
2026-06-30 07:36:00 KST info [200] PUT /api/v1/bims/20436
2026-06-30 07:36:17 KST info [204] PUT /api/v1/bims/20436/trash ← BIM trashed
2026-06-30 07:36:24 KST info [403] PUT /api/v1/bims/20436 (Api::V1::BimsController#update) — error ENT4000
trash (07:36:17) 와 agent 의 실패 호출 (07:36:22) 사이는 약 5초. 추출 작업 전체 길이는 약 25초.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 처리 중인 BIM 20436 이 사용자 trash 로 인해 mid-flight 에 사라져, 후속 GET /api/v1/rooms?bim_id=20436 가 403 ENT4000 으로 실패했고 OpenAPI 클라이언트가 HttpError("HTTP request failed") 로 래핑함 |
tesla API 로그: [204] PUT /api/v1/bims/20436/trash @ 07:36:17 → [403] PUT /api/v1/bims/20436 @ 07:36:24; agent 로그: runByMessage id:20436 @ 07:35:57 → HTTP request failed @ 07:36:22; RoomRepository#search 가 BimRepository#show 통해 untrashed scope 로 조회 (room_repository.rb:267-275); HttpError 생성 위치 확인 (apis.ts:199) |
— | Confirmed |
| H2 | tesla API 의 일반적인 인증/권한 오류 (토큰 만료 등) | — | 동일 시간대 동일 agent 의 [200] GET /api/v1/bims/20436 가 직전에 성공함, 401 이 아니라 403 + ENT4000 (NotFound) 임 |
Rejected |
| H3 | tesla API 또는 SVF2 의 일시적 외부 장애 | — | status-board 결과 dep:* active 인시던트 없음; 동일 시점 동일 endpoint 의 다른 호출은 200; 단일 BIM(20436) 에만 발생; 다른 시점 Failed to get rooms from svf2 로그는 별개 패턴(SVF2 manifest 이슈)이며 본 클러스터와 다름 |
Rejected |
| H4 | SQS 메시지 중복 처리로 인한 stale id | — | ApproximateReceiveCount: 1 (최초 수신); runByMessage id:20436 가 07:35:57 에 처음 시작됨; trash 가 그 후 발생 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
없음. 이 에러는 production impact 가 없고 단발성(occurrence_count=1) 이다. 운영적 정상 시나리오(사용자가 진행 중 BIM 을 trash) 에 대한 noise 로깅이므로 hot-fix 불필요.
단기 개선 (1주 이내)#
- 로그 레벨 다운그레이드 (race condition 시나리오):
applications/agents/packages/base/src/base-service.ts:311의handlingMessageErrors에서,getApiErrorToDeleteMessage가 반환한errorMsg의bodyResult.code가ENT4000(NotFound) 인 경우logger.error대신logger.warn으로 기록하도록 분기 추가. 이미 4xx 정책으로 메시지를 삭제하고 있으므로 alert 가치는 낮다. 메모리의 "Assess error severity during RCA" 노트와 일치하는 패턴 (cross-region / NotFound mid-flight 는 warn 이 적절). - 호출부 선제 검증:
room-service.ts:380의room.getAll(cpBim.id)호출 직전, BIM lifecycle 상태가 여전히 valid (trashed 아님) 한지 한번 더 짧게 확인하거나, 호출을 try/catch 로 감싸ENT4000인 경우 "BIM trashed during extraction" 메시지로 warn 종료. 그러면BaseService까지 에러가 올라가지 않는다.
장기 개선 (재발 방지)#
- Job-level lifecycle precondition check: SQS 메시지 처리 시작 직후,
cpBim의trashed_at또는state를 확인하여 이미 trash 인 경우 즉시 메시지 삭제하고 종료하는 가드를BaseService::run또는 각 servicerun의 입구에 추가. 현재는createCPBimByBimId에서ARG10002만 swallowing 하지만, 처리 중간의 trash 는 막을 수 없다. - OpenAPI 클라이언트 에러 메시지 개선 (선택):
HttpError("HTTP request failed")는 statusCode/uri 정보를 메시지에 포함하지 않아 fingerprint 가 너무 broad 하다. 클러스터링 품질을 위해HTTP request failed: 403 GET /api/v1/rooms처럼 식별 가능한 정보를 포함시키면 향후 분류가 쉬워진다. 단, 자동 생성 코드이므로 codegen 템플릿 변경이 필요.
Monitoring#
추가/조정 권장 메트릭 (release dashboard timeseries 위젯용 — text 코드 블록은 widget 에 그대로 들어감):
service:cupixworks-any-room-agent status:error "HTTP request failed"
service:cupixworks-any-room-agent "ENT4000" "Bim not found"
service:cupixworks-api @http.status_code:403 @http.url_details.path:"/api/v1/rooms"
알림 정책: 위 쿼리들이 단발(1건) 발생할 때는 알림 억제. 5분 내 동일 BIM 또는 facility 에 ≥ 3 건이 발생하면 그때 alert (실제 권한 회귀 또는 광범위 race condition 가능성).
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (만약 단기 개선을 적용한다면 —
ENT4000분기 1개 추가)
이 인시던트는 사용자 데이터 손실, 서비스 다운, 데이터 일관성 손상 어느 쪽도 유발하지 않는다. 단순 alert noise. 즉시 코드 변경 없이 warn 다운그레이드 정도가 가장 가성비 높은 조치다.