ES /docs

No Tasks found

RCA: No Tasks found

Overview#

What Happened#

2026-04-21 03:34~03:46 UTC에 cupixworks-sitetrack-preprocessor-agent 서비스에서 "No Tasks found" 에러가 2건 발생했다. accoes 팀(team id: 816)의 사용자 lamaya@accoes.com이 제출한 sitetrack 전처리 작업(job 1026041, 1026064)에서, 해당 sitetrack에 연결된 trackable task가 없어 createCPTasks 단계에서 예외가 발생하고 작업이 실패했다. 지난 7일간 동일 에러가 13건 발생하여 여러 팀에 걸친 반복적 문제임이 확인되었다.

Quick Facts#

Field Value
exception.class Error
exception.message No Tasks found
top_frame SiteTrackPreprocessorRunner.createCPTasks (app.cjs:9425)
runtime Node.js
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
accoes (id: 816) 3 (이번 주기 2 + 이전 1) Sitetrack 전처리 작업 실패, 후속 분석 파이프라인 미실행
geyser (id: 848) 1 (04-17) 동일한 작업 실패
기타 (04-14) ~9 다수 팀에서 동일 패턴 발생

Timeline#

  1. 2026-04-14 — 동일 에러 최초 대량 발생 (8건)
  2. 2026-04-17 19:08~23:58 — geyser 팀 등에서 2건 추가 발생
  3. 2026-04-21 01:54:04Z — accoes 팀 sitetrack 16679에서 1건 발생
  4. 2026-04-21 03:34:11Z — 이번 클러스터 첫 번째 에러 (job 1026041, sitetrack 16685)
  5. 2026-04-21 03:46:14Z — 이번 클러스터 두 번째 에러 (job 1026064, sitetrack 16688)
  6. 2026-04-21 — RCA 수행

Error Log#

Datadog Logs

text
No Tasks found

Impact#

  • Service: cupixworks-sitetrack-preprocessor-agent
  • Team: accoes
  • 발생 횟수: 2 (이번 클러스터), 13 (지난 7일 전체)
  • 최초 발생: 2026-04-21T03:34:11.659Z
  • 최근 발생: 2026-04-21T03:46:14.863Z

Root Cause Summary#

Sitetrack 전처리 agent의 createCPTasks 메서드는 API에서 가져온 task 목록을 category와 workarea 기준으로 필터링한 후, isTrackable 조건(task의 start_at 기준 30일 이내에 record가 캡처되었는지)을 추가로 검사하여 trackable task를 결정한다. 문제의 sitetrack들은 이 두 단계의 필터링을 모두 통과하는 task가 0건이어서, 코드에 의도적으로 설정된 validation(cpTrackableTasks.length === 0)에 의해 Error("No Tasks found")가 throw되었다. 이는 코드 버그가 아니라, task가 아직 설정되지 않았거나 record 캡처 시점 기준으로 활성 task가 없는 sitetrack에 대해 전처리 작업이 요청된 데이터 구성 문제이다. 동일 시간대 성공한 job(1026060, sitetrack 16683)은 task가 존재하여 정상 완료되었다.

Technical Analysis#

Code Path#

  • Entry point: preprocessor-service.ts:49PreprocessorService.run()이 SQS 메시지를 수신하여 job을 로드하고 처리 시작
cupix-sitetrack-preprocessor-agent/src/preprocessor-service.ts:49-71typescript
run = async (targetId: number, msgObject: any): Promise<void> => {
    logger.info('PreprocessorService::run | begin');
    try {
        const srvJob = await this.jobManager.loadJob(targetId);
        // ...
        await this.jobManager.updateJobProgress(7, 'load_target_models');
        await runner.createTargetModels(cpSitetrack);
  • createTargetModels에서 createCPTasks를 호출: sitetrack-preprocessor-runner.ts:21
cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:9-21typescript
async createTargetModels(cpSitetrack: CPSitetrack): Promise<void> {
    // ... cpBims, cpCaptures, cpElements 등 로드
    const cpCategories = await this.loadAllCategories(cpSitetrack);
    const cpElements = await this.createCPElements(cpSitetrack, uniqueLevelIds, cpBims, cpCategories);
    await this.createCPTasks(cpSitetrack, cpCategories, cpElements);
  • Failure point: sitetrack-preprocessor-runner.ts:182-184 — trackable task가 0건일 때 에러 throw
cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:159-190typescript
async createCPTasks(cpSitetrack: CPSitetrack, cpCategories: CPCategory[], cpElements: CPElement[]): Promise<CPTask[]> {
    // 1단계: API에서 전체 task 조회
    const srvTasks = await this.cupixApi.task.getAll(cpSitetrack.facilityKey);
    // 2단계: category + workarea 기준 필터링
    const filteredTasks = srvTasks.filter(task =>
        uniqueCategoryIds.includes(task.category?.id ?? -1)
        && uniqueWorkareaIds.includes(task.workarea?.id ?? -1)
    );
    // 3단계: CPTask 모델 생성 (이때 isTrackable 판정 수행)
    const cpTasks = await createModels(CPTask, cpSitetrack, Promise.resolve(filteredTasks));
    // ...
    // 4단계: trackable task 검사 — 여기서 실패
    if (cpSitetrack.cpTrackableTasks.length === 0) {
        this.jobManager.setErrorCode(ErrorCode.Agent.DeviationTracksNotFound);
        throw new Error('No Tasks found');
    }
  • isTrackable 판정 로직: cptask.ts:62-67 — task의 start_at에서 30일(ActivityPrestartDays)을 뺀 날짜보다 record의 captured_at이 이전이면 trackable이 아님
models/src/cptask.ts:62-67typescript
setCapturedAfterTaskStart = (recordCapturedAt?: Date): void => {
    if (recordCapturedAt === undefined) this._isTrackable = false;
    else if (this.id == UnknownId) this._isTrackable = false;
    else if (!this.activityStartAt || !this.activityEndAt) this._isTrackable = false;
    else if (isEarlierISODateThan(recordCapturedAt, CPUtils.addDays(this.activityStartAt, -ActivityPrestartDays), true)) this._isTrackable = false;
    else this._isTrackable = true;
  • cpTrackableTasks getter: cpsitetrack.ts:77_cpTasks 배열에서 isTrackable === true인 것만 필터
models/src/cpsitetrack.ts:77typescript
get cpTrackableTasks(): Array<CPTask> { return this._cpTasks.filter(cpTask => cpTask.isTrackable); }

기대 동작: Sitetrack에 연결된 facility에 category/workarea가 매칭되고 record 캡처 시점 기준 활성화된 task가 1개 이상 존재하여 전처리가 진행됨.

실제 동작: 문제의 sitetrack(16685, 16688)에는 필터링 조건을 통과하는 trackable task가 0건이어서 validation에서 에러가 throw됨. 에러는 catch되어 updateErrorActionJob으로 처리되고, SQS 메시지는 삭제됨.

Log Evidence#

에러 발생 전후 실행 흐름 (Job 1026041):

text
Datadog query: service:cupixworks-sitetrack-preprocessor-agent @job.id:1026041
text
03:34:03.364Z [info]  BaseService::runByMessage | id: 1026041
03:34:03.454Z [info]  CupixAuth::setSession | session_id: ebb3170b...
03:34:03.455Z [info]  PreprocessorService::run | begin
03:34:03.455Z [info]  JobManager::loadJob | begin - job id: 1026041
03:34:03.505Z [info]  JobManager::loadJob | end - job id: 1026041
03:34:04.103Z [info]  JobManager::updateJobProgress | begin - jobId: 1026041, processingStatus: load_sitetrack_on_preprocessor
03:34:04.104Z [warn]  JobManager::updateJobProgress | skipped - progress: 5, status: load_sitetrack_on_preprocessor
03:34:04.104Z [info]  PreprocessorService::createCPSitetrack | begin - sitetrackId: 16685
03:34:04.137Z [info]  PreprocessorService::createCPSitetrack | end
03:34:04.140Z [info]  JobManager::updateJobProgress | begin - jobId: 1026041, processingStatus: load_target_models
03:34:11.659Z [error] No Tasks found
03:34:11.974Z [info]  PreprocessorService::run | end
03:34:11.974Z [info]  BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/16685
03:34:12.003Z [info]  AwsQueueManager::deleteMessage | end

동일 시간대 성공한 job(1026060, sitetrack 16683)과의 비교:

text
Datadog query: service:cupixworks-sitetrack-preprocessor-agent @job.id:1026060
text
03:45:14.303Z [info]  BaseService::runByMessage | id: 1026060
03:45:14.808Z [info]  PreprocessorService::createCPSitetrack | begin - sitetrackId: 16683
03:45:14.868Z [info]  JobManager::updateJobProgress | processingStatus: load_target_models
03:45:29.942Z [warn]  CPElement::findTask | This element will not be tracked because task not found... (x38)
03:45:34.632Z [info]  JobManager::updateJobProgress | processingStatus: download_files
03:45:52.429Z [info]  JobManager::updateJobProgress | processingStatus: create_request_json
03:45:52.757Z [info]  JobManager::updateJobProgress | processingStatus: save_file_system
03:45:52.972Z [info]  FileSystemManager::saveCPSiteinsightsModel | Save total 2608 cpElementRecords
03:46:04.877Z [info]  PreprocessorService::run | end

핵심 차이: 성공한 job은 load_target_models 이후 38개의 element-task 매칭 실패 경고가 있었으나, 전체적으로 task가 존재하여 download_filescreate_request_jsonsave_file_system으로 정상 진행되었다. 실패한 job은 load_target_models 직후 "No Tasks found"로 즉시 종료되었다.

에러 스택 트레이스 (두 건 모두 동일):

text
Error: No Tasks found
    at SiteTrackPreprocessorRunner.createCPTasks (/tmp/agent/dist/app.cjs:9425:13)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async descriptor.value (/tmp/agent/dist/app.cjs:817:30)
    at async SiteTrackPreprocessorRunner.createTargetModels (/tmp/agent/dist/app.cjs:9290:5)
    at async descriptor.value (/tmp/agent/dist/app.cjs:817:30)
    at async PreprocessorService.run (/tmp/agent/dist/app.cjs:9643:13)
    at async BaseService2.runByMessage (/tmp/agent/dist/app.cjs:6012:15)

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Sitetrack에 trackable task가 0건인 데이터 구성 문제 — facility에 task가 아예 없거나 category/workarea 필터링 후 0건, 또는 isTrackable 시간 조건 불충족 코드 sitetrack-preprocessor-runner.ts:170-184에서 3단계 필터링 (API 조회 → category/workarea 필터 → isTrackable 시간 판정) 확인. 성공 job(16683)은 task 존재, 실패 job(16685, 16688)은 0건. 13건이 다수 팀에 걸쳐 반복 Confirmed
H2 API 호출 실패로 task 목록이 빈 배열로 반환됨 에러 메시지가 네트워크/API 에러가 아닌 validation 에러. task.getAll 호출이 실패했다면 다른 예외가 발생했을 것. 동일 시간대 다른 job은 정상적으로 task를 조회함 Rejected
H3 isTrackable 로직의 시간 비교 버그 (ActivityPrestartDays=30일 계산 오류) cptask.ts:66의 날짜 비교 로직이 복잡함 isEarlierISODateThan은 단순 ISO 문자열 비교이며 단위 테스트로 검증됨. 동일 시간대 다른 job에서 정상 작동함. 문제의 sitetrack에 task 자체가 없거나 시간 범위 밖일 가능성이 더 높음 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • sitetrack-preprocessor-runner.ts:182-184에서 "No Tasks found" 발생 시 error 레벨 대신 warn 레벨로 로그를 남기고, 에러를 throw하지 않고 조기 반환(early return)하는 방식으로 변경을 검토할 것. 현재 이 에러는 preprocessor-service.ts:94-96의 catch 블록에서 잡혀 updateErrorActionJob으로 처리되므로 서비스 자체는 계속 작동하지만, 불필요한 error 로그가 발생하고 job이 error 상태로 마킹됨.
  • 단, job을 error로 마킹하는 것이 의도된 동작(사용자에게 task 미설정을 알림)일 수 있으므로, 팀과 논의하여 error throw를 유지하되 로그 레벨만 warn으로 낮출지, 아니면 graceful하게 처리할지 결정 필요.

단기 개선 (1주 이내)#

  • createCPTasks에서 필터링 전후의 task 수를 info 레벨로 로깅 추가: srvTasks.length(API 원본), filteredTasks.length(category/workarea 필터 후), cpTasks.length(모델 생성 후), cpTrackableTasks.length(isTrackable 필터 후). 현재는 debug 레벨로만 기록되어 Datadog에서 확인 불가.
  • task가 0건인 경우의 error code를 AGT1809(DeviationTracksNotFound)로 설정하는 것은 이미 구현되어 있으므로, 이를 활용하여 API 측에서 사용자에게 적절한 안내 메시지를 표시하는지 확인.

장기 개선 (재발 방지)#

  • Sitetrack 전처리 작업 제출 전에 facility에 trackable task가 존재하는지 사전 검증하는 validation을 API 또는 프론트엔드 레벨에서 추가. Task가 없는 sitetrack에 대해 전처리 요청 자체를 차단하면 불필요한 agent 리소스 사용을 방지할 수 있음.
  • 에러 심각도를 재분류하여 이 유형의 에러가 모니터링 알림을 트리거하지 않도록 할 것 — 이는 시스템 장애가 아닌 데이터 구성 이슈임.

Monitoring#

  • 다음 Datadog 쿼리로 발생 빈도 추적:
text
service:cupixworks-sitetrack-preprocessor-agent status:error "No Tasks found" @environment:production
  • Error code AGT1809 기준 모니터링:
text
service:cupixworks-sitetrack-preprocessor-agent @error_code:AGT1809
  • 특정 팀에서 반복적으로 발생하는지 팀별 집계 추적:
text
service:cupixworks-sitetrack-preprocessor-agent status:error "No Tasks found" @team.domain:*

Risk Assessment#

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