ES /docs

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

RCA: BaseService::handlingMessageErrors | 403 Bim not found

Overview#

What Happened#

2026-04-22 05:00:15 UTC (KST 14:00:15)에 cupixworks-any-mesh-agent 서비스가 BIM 3311의 mesh extraction 처리 중 403 Forbidden 에러를 발생시켰다. 에이전트가 SQS 메시지를 수신하여 BIM 데이터를 성공적으로 조회한 후, 약 90초 뒤에 다른 프로세스가 해당 BIM을 trash 처리했고, 이후 에이전트의 후속 API 호출이 모두 실패했다.

Quick Facts#

Field Value
exception.class HttpError
exception.message HTTP request failed (statusCode: 403, Bim not found, ENT4000)
top_frame @tesla/typescript-node-sdk/api/bimApi.js:1189
env production, ap-southeast-2

Timeline#

  1. 13:58:34 KST — BIM 3311에 대한 정상적인 API 호출 시작 (PUT update 등)
  2. 13:58:36 KST — Mesh agent가 SQS 메시지 수신, BaseService::runByMessage | id: 3311 로그
  3. 13:58:38 KST — Agent가 GET /api/v1/bims/3311 호출, 200 OK 응답 수신
  4. 13:58:42 KST — Agent가 PUT /api/v1/bims/3311 (mesh_state update), 200 OK
  5. 13:59:09 KSTBIM 3311이 trash 처리됨 (PUT /api/v1/bims/3311/trash, 204 응답)
  6. 14:00:15-14:00:17 KST — Agent의 후속 API 호출 실패 (403 ENT4000 "Bim not found")
  7. 14:00:15 KSTBaseService::handlingMessageErrors에서 에러 로깅, SQS 메시지 삭제

Error Log#

Datadog Logs

text
BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":403,"requestUriHref":"http://api-tesla.cupix.internal/api/v1/bims/3311?fields%5B0%5D=id&fields%5B1%5D=name&fields%5B2%5D=state&fields%5B3%5D=mesh_state&fields%5B4%5D=facility&fields%5B5%5D=forge_urn&fields%5B6%5D=forge_format_type&fields%5B7%5D=forge_region&fields%5B8%5D=grid_system_state&fields%5B9%5D=grid_system_upload_url&fields%5B10%5D=bim_source&fields%5B11%5D=meta&fields%5B12%5D=facility&fields%5B13%5D=bim_objects_url&fields%5B14%5D=origin&fields%5B15%5D=offset&fields%5B16%5D=rotate_z&fields%5B17%5D=transform_order&fields%5B18%5D=fresh_state&fields%5B19%5D=bim_objects_url&fields%5B20%5D=created_at&fields%5B21%5D=published_at&fields%5B22%5D=resource_state&fields%5B23%5D=forge_state&fields%5B24%5D=room_state&fields%5B25%5D=encoded_forge_urn&fields%5B26%5D=forge_workflow_id&fields%5B27%5D=last_bim_revision&fields%5B28%5D=thumbnail_urls&fields%5B29%5D=siteinsights_entities_created_at","bodyResult":{"code":"ENT4000","type":"Cupix::Errors::NotFound","reason":"Bim not found","message":"Bim not found"},"modelId":3311},"sqsMessage":{"MessageId":"1c9b7676-0594-4af3-9fee-4a6213f4dd08","Attributes":{"ApproximateReceiveCount":"1"}}}

Impact#

  • Service: cupixworks-any-mesh-agent
  • Team: built
  • 발생 횟수: 1
  • 최초 발생: 2026-04-22T05:00:15.781Z
  • 최근 발생: 2026-04-22T05:00:15.781Z

BIM 3311의 mesh extraction 작업이 실패했다. 다만 BIM이 이미 trash 처리되었으므로 사용자에게 실질적인 기능 영향은 없다. SQS 메시지는 정상적으로 삭제되어 무한 재시도는 발생하지 않았다.

Root Cause Summary#

Mesh agent가 SQS 메시지를 수신하여 BIM 3311의 mesh extraction을 시작한 후, 처리 도중 (약 90초 후) 사용자 또는 다른 프로세스가 해당 BIM을 trash 처리했다. Tesla API는 trashed된 리소스에 대해 visibility_scope(UNTRASHED) 필터로 인해 Cupix::Errors::NotFound (code: ENT4000)를 발생시키며, 이는 보안 정책상 403 Forbidden으로 반환된다. Mesh agent의 createCPBimByBimId에서 error code ARG10002(리소스 미존재)만 graceful하게 처리하고, ENT4000(trashed 리소스)은 처리하지 않아 에러로 전파되었다.

Technical Analysis#

Code Path#

1. Entry point — SQS 메시지 수신 및 처리 시작

applications/agents/packages/base/src/base-service.ts:153-172typescript
protected runByMessage = async (message: AWS.SQS.Message) => {
    this._messageInProcess = message;
    const messageBody = message.Body;
    if (messageBody && CPUtils.isJsonString(messageBody)) {
        const msgObject = JSON.parse(messageBody);
        const targetId = Environment.DEBUG_MODE && Environment.CPX_MODEL_ID
            ? Environment.CPX_MODEL_ID
            : (msgObject.id ?? msgObject.model?.id);
        // ...
        logger.info('BaseService::runByMessage | id: %d', targetId);
        await this.authenticateByMessage(msgObject);
        await this.run(targetId, msgObject);  // MeshService.run() 호출
        await this.cleanUpAnythingRelatedModel();
        await this.deleteByMessage(message);
    }
};

Agent가 BIM ID 3311로 run() 호출. 이 시점에서 BIM은 아직 정상 상태.

2. MeshService::run — BIM 조회 및 mesh extraction 시작

applications/agents/packages/cupix-tesla-mesh-agent/src/mesh-service.ts:101-122typescript
protected run = async (targetId: number): Promise<void> => {
    const cpBim = await this.createCPBimByBimId(targetId);
    if (cpBim) {
        this._modelInProcess = cpBim;
        this._forgeApi.updateRegionFromBim(cpBim.forgeRegion);
        await this.updateBimMeshState(targetId, TESLA.UpdateBimRequest.MeshStateEnum.Extracting);
        try {
            await this.forgeAuth.authenticate();
            await this.runMeshExtractor(cpBim);  // child process 실행
            await cpBim.createMeshArchive();
            await cpBim.checkMeshFiles();
            await this.uploadMeshArchiveFileByCPBim(cpBim);  // 여기서 upload credential 요청 시 403 발생
            await this.updateBimMeshState(targetId, TESLA.UpdateBimRequest.MeshStateEnum.Extracted);
        } catch (error) {
            logger.error('MeshService::run | error: %s', JSON.stringify(error, Object.getOwnPropertyNames(error)));
            await this.updateErrorState(cpBim);
        }
    }
};

createCPBimByBimId(3311) 호출 시 (13:58:38 KST) BIM이 정상이므로 200 OK 수신. 이후 mesh extraction child process (runMeshExtractor) 실행 중 13:59:09 KST에 BIM이 trash 처리됨. 이후 uploadMeshArchiveFileByCPBim 또는 후속 API 호출에서 403 발생.

3. Failure point — createCPBimByBimId의 에러 코드 분기

applications/agents/packages/cupix-tesla-mesh-agent/src/mesh-service.ts:131-149typescript
private createCPBimByBimId = (bimId: number): Promise<CPBim | undefined> =>
    new Promise((resolve, reject) => {
        this.cupixApi.bim.get(bimId)
            .then(resBim => {
                resolve(new CPBim(resBim));
            })
            .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('MeshService::createCPBimByBimId | end - reason: %s', reason);
                    if (code === 'ARG10002') return resolve(undefined);  // 리소스 미존재만 graceful 처리
                }
                reject(ec);  // ENT4000 (trashed)은 여기로 — reject 발생
            });
    });

ARG10002 (리소스가 DB에 존재하지 않음)만 resolve(undefined)로 graceful 처리하고, ENT4000 (trashed된 리소스)은 reject(ec)로 에러 전파. 다만 이 에러는 초기 GET이 아닌 후속 API 호출(upload_credentials, update 등)에서 발생했다.

4. Tesla API의 403 반환 로직

app/controllers/concerns/client_error_controller.rb:27ruby
rescue_from Cupix::Errors::NotFound, with: :not_found_403_error
app/repositories/base_repository.rb:347-352ruby
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

BIM 3311이 trash 상태이므로 visibility_scope(UNTRASHED) 필터에서 제외되고, in_trash 확인에서 true → ENT4000 코드로 NotFound 발생 → 403 반환.

5. Error handler — SQS 메시지 삭제 결정

applications/agents/packages/base/src/base-service.ts:270-275typescript
if (statusCode != undefined && statusCode >= 400 && statusCode <= 500) {
    if (statusCode === 401) return;  // 401만 재시도
    return errorMsg;  // 403 포함 나머지 4xx → 메시지 삭제
}

403은 4xx 범위이므로 getApiErrorToDeleteMessage가 errorMsg를 반환 → SQS 메시지 삭제. 재시도 없이 종료.

Log Evidence#

Datadog에서 BIM 3311 관련 API 로그를 시간순으로 조회:

text
service:cupixworks-api "bims/3311"
Time range: 2026-04-22T04:00:00Z to 2026-04-22T06:00:00Z

핵심 이벤트:

json
{"timestamp": "2026-04-22 13:58:38 KST", "status": "info", "message": "[200] GET /api/v1/bims/3311 (Api::V1::BimsController#show)"}

Agent의 초기 BIM 조회 — 정상 응답.

json
{"timestamp": "2026-04-22 13:59:09 KST", "status": "info", "message": "[204] PUT /api/v1/bims/3311/trash (Api::V1::BimsController#trash)"}

BIM 3311 trash 처리 — agent 처리 도중 발생. 이것이 race condition의 trigger.

json
{"timestamp": "2026-04-22 14:00:15 KST", "status": "error", "message": "MeshService::run | error: {\"stack\":\"HttpError: HTTP request failed\\n    at Request._callback (/tmp/agent/dist/node_modules/@tesla/typescript-node-sdk/api/bimApi.js:1189:40)...\",\"message\":\"HTTP request failed\",\"statusCode\":403}"}

Agent의 후속 API 호출 실패.

json
{"timestamp": "2026-04-22 14:00:16 KST", "status": "info", "message": "[403] POST /api/v1/bims/3311/resources/mesh/upload_credentials (Api::V1::BimsController#resource_upload_credentials)", "error": {"code": "ENT4000", "class": "Cupix::Errors::NotFound"}}

Upload credential 요청이 403으로 실패 — BIM이 trash 상태이므로 ENT4000.

json
{"timestamp": "2026-04-22 14:00:15 KST", "status": "warn", "message": "BaseService::getApiErrorToDeleteMessage | error msg - {\"statusCode\":403,...,\"bodyResult\":{\"code\":\"ENT4000\",...},\"modelId\":3311}"}

Error handler가 403을 감지하고 메시지 삭제 결정.

Agent 측 전체 흐름:

text
service:cupixworks-any-mesh-agent "3311"
json
{"timestamp": "2026-04-22 13:58:36 KST", "status": "info", "message": "BaseService::runByMessage | id: 3311"}
json
{"timestamp": "2026-04-22 14:00:15 KST", "status": "warn", "message": "BaseService::getApiErrorToDeleteMessage | error msg - {...\"statusCode\":403,...\"code\":\"ENT4000\"...}"}
json
{"timestamp": "2026-04-22 14:00:15 KST", "status": "error", "message": "BaseService::handlingMessageErrors | Error and message object - {...}"}

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 BIM 3311이 agent 처리 도중 trash되어 race condition 발생 API 로그에서 13:59:09에 PUT /bims/3311/trash (204) 확인. 이후 모든 API 호출이 ENT4000으로 실패. Agent는 13:58:36에 처리 시작, 13:58:38에 GET 성공(200). Confirmed
H2 Agent의 인증 토큰 만료로 403 발생 403 응답 코드가 일치 에러 body의 code가 ENT4000(NotFound)이지 AUTH 계열이 아님. 같은 시간대 다른 agent 작업은 정상. 401이 아닌 403이며, Tesla API는 인증 실패 시 401을 반환. Rejected
H3 BIM 3311이 원래 존재하지 않는 리소스 403 상태 코드 일치 API 로그에서 13:58:34-13:58:42에 BIM 3311에 대한 여러 성공적인 API 호출(200) 확인. 에러 코드가 ARG10002(미존재)가 아닌 ENT4000(trashed). Rejected

Fix Recommendation#

즉시 조치 (Critical)#

없음. 이 에러는 BIM이 정당하게 trash 처리된 후 발생한 것으로, 사용자에게 실질적 영향이 없다. SQS 메시지도 정상 삭제되어 무한 재시도가 발생하지 않는다.

단기 개선 (1주 이내)#

  • mesh-service.ts:131-149: createCPBimByBimId의 catch 블록에서 ENT4000 코드도 ARG10002와 동일하게 resolve(undefined)로 처리. Trashed된 BIM에 대해서는 mesh extraction을 건너뛰고 graceful하게 종료해야 한다.
  • mesh-service.ts:101-122: run() 메서드의 후속 API 호출(uploadMeshArchiveFileByCPBim, updateBimMeshState) 실패 시에도 ENT4000을 감지하여 error state update 없이 조기 종료하는 로직 추가.
  • 로그 레벨 조정 검토: Trashed BIM에 대한 403은 예상 가능한 운영 시나리오이므로 error가 아닌 warn 수준이 적절할 수 있다. BaseService::handlingMessageErrors에서 ENT4000 응답을 별도 분기하여 warn으로 로깅.

장기 개선 (재발 방지)#

  • Optimistic locking 또는 상태 체크: Agent가 장기 처리 작업 전에 BIM 상태를 재확인하는 패턴 도입. 예: mesh extraction 완료 후, upload 전에 BIM 상태를 다시 조회.
  • SQS 메시지에 상태 정보 포함: SQS 메시지 발행 시 BIM의 현재 상태를 포함하여, agent가 처리 시작 시 이미 trashed된 BIM을 즉시 건너뛸 수 있도록 개선.

Monitoring#

현재 발생 빈도가 1건으로 낮아 긴급 모니터링은 불필요하다. 단, ENT4000 graceful 처리가 구현된 후 에러 감소를 확인하기 위해:

text
service:cupixworks-any-mesh-agent status:error "ENT4000"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial — createCPBimByBimId의 catch 블록에 ENT4000 조건 추가만으로 해결 가능