BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":403,"requestU
RCA: BaseService::handlingMessageErrors | 403 Bim not found (ENT4000)
Error Log#
BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":403,"requestUriHref":"http://api-tesla.cupix.internal/api/v1/bims/18914?fields%5B0%5D=id&fields%5B1%5D=name&fields%5B2%5D=state&...","bodyResult":{"code":"ENT4000","type":"Cupix::Errors::NotFound","reason":"Bim not found","message":"Bim not found"},"modelId":18914},"sqsMessage":{"MessageId":"797d6620-c495-4954-9b26-f53248fb3378","Attributes":{"ApproximateReceiveCount":"1"}}}
Impact#
- Service:
cupixworks-any-room-agent - Team: wgyates
- 발생 횟수: 2
- 최초 발생: 2026-04-07T18:58:02.452Z
- 최근 발생: 2026-04-07T18:58:31.474Z
Root Cause Summary#
BIM 18914가 SQS 메시지 큐에 등록된 이후, 사용자가 해당 BIM을 trash 처리(soft-delete)하여 에이전트가 API 호출 시 ENT4000 (Bim not found) 에러를 받았습니다. RoomService::createCPBimByBimId는 ARG10002 (레코드 미존재) 코드만 graceful하게 처리하고, ENT4000 (trashed 상태) 코드는 처리하지 않아 에러로 전파되었습니다. 동일한 BIM에 대해 cupixworks-any-room-agent와 cupixworks-any-mesh-agent 두 서비스에서 동시에 발생했으며, 에이전트는 SQS 메시지를 삭제하고 room_state를 Error로 설정했습니다. 이 에러는 사용자 행동(BIM trash)에 의한 일시적 race condition이며, 데이터 손실이나 시스템 장애를 유발하지는 않지만, 불필요한 에러 로그를 생성합니다.
Technical Analysis#
Code Path#
- Entry point:
base-service.ts:86—checkingQueue()에서 SQS 폴링 시작 base-service.ts:108—runByMessages()호출, 각 메시지를 순회하며 처리base-service.ts:153—runByMessage(message)— SQS 메시지 body에서targetId(BIM ID 18914) 추출room-service.ts:58—run(targetId)호출,createCPBimByBimId(targetId)로 BIM 조회
// applications/agents/packages/cupix-tesla-room-agent/src/room-service.ts:102-120
.catch(ec => {
const body = ec?.response?.body;
if (body) {
const code = body.result?.code;
const reason = body.result?.reason ? body.result.reason : body.result?.message;
logger.warn('RoomService::createCPBimByBimId | end - reason: %s', reason);
if (code === 'ARG10002') return resolve(undefined); // 레코드 미존재만 처리
}
reject(ec); // ENT4000 (trashed)는 여기서 reject됨
});
bim.api.ts:40-44—BimApiModule.get(bimId)가GET /api/v1/bims/18914를 호출
// applications/agents/packages/api/src/api/bim.api.ts:40-44
get = async (bimId: number): Promise<TESLA.Bim> => {
const api = await this.api();
const res = await api.getBim(bimId, Fields.BimFields);
return unwrapAttributes(res);
};
- Rails API에서 trashed BIM에 대해
ENT4000에러를 403으로 반환:
# tesla/app/repositories/base_repository.rb:347-352
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
# tesla/app/controllers/concerns/client_error_controller.rb:27
rescue_from Cupix::Errors::NotFound, with: :not_found_403_error
NotFound예외가 의도적으로 403으로 매핑됨 (resource enumeration 방지 보안 설계)- Failure point:
room-service.ts:118—ENT4000코드가 처리되지 않고reject(ec)실행 base-service.ts:110— catch에서handlingMessageErrors호출base-service.ts:290-313— 403이 400-500 범위에 해당하므로 SQS 메시지 삭제 후 에러 로깅
// applications/agents/packages/base/src/base-service.ts:290-313
private handlingMessageErrors = async (error: any): Promise<void> => {
const errorAndMessage = { error: error, sqsMessage: {} };
if (this.messageInProcess) {
errorAndMessage.sqsMessage = {
MessageId: this.messageInProcess.MessageId,
Attributes: this.messageInProcess.Attributes
};
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 | Errors in error handling', error);
}
}
}
logger.error('BaseService::handlingMessageErrors | Error and message object - %s', JSON.stringify(errorAndMessage));
};
기대 동작: trashed BIM에 대한 SQS 메시지를 graceful하게 무시 (warn 로그만 남기고 정상 종료)
실제 동작: ENT4000이 처리되지 않아 에러로 전파, SQS 메시지 삭제 + room_state Error 설정 + 에러 로그 발생
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-any-room-agent status:error
(time: 2026-04-07T17:58:02Z to 2026-04-07T19:28:31Z)
18914
(time: 2026-04-07T17:58:02Z to 2026-04-07T19:28:31Z)
service:cupixworks-any-room-agent status:info
(time: 2026-04-07T17:58:02Z to 2026-04-07T19:28:31Z)
타임라인:
| Time (UTC) | Service | Level | Event |
|---|---|---|---|
| 18:55:25.077 | cupixworks-api | info | [200] PUT /api/v1/bims/18914 — 에이전트 세션에서 BIM 업데이트 |
| 18:55:34.269 | cupixworks-api | info | [204] PUT /api/v1/bims/18914/trash — 사용자가 브라우저에서 BIM trash 처리 |
| 18:55:33.943 | notification-service | info | Feed handler: delete/trashed event, BIM 18914, mesh_state=queued, room_state=queued |
| 18:58:02.343 | cupixworks-any-room-agent | info | BaseService::runByMessage | id: 18914 — SQS 메시지 처리 시작 |
| 18:58:02.390 | cupixworks-any-room-agent | info | CupixAuth::setSession — 세션 설정 |
| 18:58:02.421 | cupixworks-any-room-agent | warn | BaseService::getApiErrorToDeleteMessage — 403 Bim not found 감지 |
| 18:58:02.422 | cupixworks-any-room-agent | info | AwsQueueManager::deleteMessage | begin — SQS 메시지 삭제 시작 |
| 18:58:02.451 | cupixworks-any-room-agent | info | AwsQueueManager::deleteMessage | end — SQS 메시지 삭제 완료 |
| 18:58:02.452 | cupixworks-any-room-agent | error | BaseService::handlingMessageErrors — 에러 로그 기록 |
| 18:58:31.474 | cupixworks-any-mesh-agent | error | 동일 에러 (다른 SQS 메시지 b1ae7ed4) |
핵심 로그 — 사용자의 trash 요청 (원인):
{
"http": { "status_code": 204, "method": "PUT", "url": "/api/v1/bims/18914/trash" },
"usr": { "id": "34202", "name": "Manpreet Lidder", "email": "manpreet.lidder@wgyates.com" },
"network": { "client": { "ip": "3.172.37.210" } },
"http_user_agent": "Chrome/146 Edge/146 (Windows)"
}
핵심 로그 — notification-service에서 확인된 BIM 상태:
{
"message": "Feed handler: action=delete, reason=trashed, model_type=Bim, model_id=18914",
"bim": { "name": "AMPLIFY_HER_300A_HVAC.rvt", "state": "done", "bim_source": "acc", "mesh_state": "queued", "room_state": "queued" },
"facility": { "id": 13822, "key": "ercgqs", "name": "100-600 Main Building" }
}
핵심 로그 — API의 403 응답:
{
"http": { "status_code": 403, "method": "GET", "url": "/api/v1/bims/18914" },
"error": { "code": "ENT4000", "class": "Cupix::Errors::NotFound", "reason": "Bim not found" },
"controller": "Api::V1::BimsController#show"
}
Fix Recommendation#
즉시 조치 (Critical)#
room-service.ts:102-120의 createCPBimByBimId catch 핸들러에서 ENT4000 코드도 ARG10002와 동일하게 graceful하게 처리해야 합니다. trashed BIM은 더 이상 처리가 필요 없으므로 undefined를 반환하고 정상 종료하면 됩니다.
동일한 패턴이 cupixworks-any-mesh-agent에도 존재할 가능성이 높으므로 해당 서비스의 코드도 함께 수정해야 합니다.
단기 개선 (1주 이내)#
BaseService 레벨에서 ENT4000 (trashed resource)에 대한 공통 처리 로직을 추가하는 것을 검토해야 합니다. 모든 에이전트(room, mesh 등)가 동일한 race condition에 노출되므로, 개별 서비스마다 처리하는 것보다 BaseService::getApiErrorToDeleteMessage 또는 runByMessage에서 trashed resource를 공통으로 처리하면 일관성이 보장됩니다. 이때 에러 레벨이 아닌 warn 레벨로 로깅하여 노이즈를 줄여야 합니다.
장기 개선 (재발 방지)#
BIM이 trash 처리될 때 관련 SQS 큐의 메시지를 무효화하거나, BIM trash 시 room_state/mesh_state가 queued인 경우 해당 큐 메시지를 정리하는 로직을 Rails API 측에 추가하면 근본적으로 이 race condition을 방지할 수 있습니다.
Monitoring#
- trashed BIM에 의한 불필요한 에러 감소를 추적하기 위한 Datadog 쿼리:
service:(cupixworks-any-room-agent OR cupixworks-any-mesh-agent) status:error "ENT4000"
- 수정 후 해당 쿼리 결과가 0이 되는지 확인
- 기존 에러 알림에서
ENT4000이 포함된handlingMessageErrors를 제외하거나 별도 warn 모니터로 분리
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial