ES /docs

BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":403,"requestU

RCA: BaseService::handlingMessageErrors | 403 Bim not found (ENT4000)

Error Log#

Datadog Logs

text
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::createCPBimByBimIdARG10002 (레코드 미존재) 코드만 graceful하게 처리하고, ENT4000 (trashed 상태) 코드는 처리하지 않아 에러로 전파되었습니다. 동일한 BIM에 대해 cupixworks-any-room-agentcupixworks-any-mesh-agent 두 서비스에서 동시에 발생했으며, 에이전트는 SQS 메시지를 삭제하고 room_state를 Error로 설정했습니다. 이 에러는 사용자 행동(BIM trash)에 의한 일시적 race condition이며, 데이터 손실이나 시스템 장애를 유발하지는 않지만, 불필요한 에러 로그를 생성합니다.

Technical Analysis#

Code Path#

  • Entry point: base-service.ts:86checkingQueue()에서 SQS 폴링 시작
  • base-service.ts:108runByMessages() 호출, 각 메시지를 순회하며 처리
  • base-service.ts:153runByMessage(message) — SQS 메시지 body에서 targetId (BIM ID 18914) 추출
  • room-service.ts:58run(targetId) 호출, createCPBimByBimId(targetId)로 BIM 조회
typescript
// 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-44BimApiModule.get(bimId)GET /api/v1/bims/18914를 호출
typescript
// 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으로 반환:
ruby
# 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
ruby
# 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:118ENT4000 코드가 처리되지 않고 reject(ec) 실행
  • base-service.ts:110 — catch에서 handlingMessageErrors 호출
  • base-service.ts:290-313 — 403이 400-500 범위에 해당하므로 SQS 메시지 삭제 후 에러 로깅
typescript
// 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 쿼리:

text
service:cupixworks-any-room-agent status:error
(time: 2026-04-07T17:58:02Z to 2026-04-07T19:28:31Z)
text
18914
(time: 2026-04-07T17:58:02Z to 2026-04-07T19:28:31Z)
text
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 요청 (원인):

json
{
  "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 상태:

json
{
  "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 응답:

json
{
  "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-120createCPBimByBimId 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_statequeued인 경우 해당 큐 메시지를 정리하는 로직을 Rails API 측에 추가하면 근본적으로 이 race condition을 방지할 수 있습니다.

Monitoring#

  • trashed BIM에 의한 불필요한 에러 감소를 추적하기 위한 Datadog 쿼리:
text
service:(cupixworks-any-room-agent OR cupixworks-any-mesh-agent) status:error "ENT4000"
  • 수정 후 해당 쿼리 결과가 0이 되는지 확인
  • 기존 에러 알림에서 ENT4000이 포함된 handlingMessageErrors를 제외하거나 별도 warn 모니터로 분리

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial