ES /docs

InvalidParameterException on task_id: arn:aws:ecs:us-west-2:619071347432:task/cupix-tesla-ece/48ecc8

RCA: InvalidParameterException on ECS task_id (task not found)

Overview#

What Happened#

2026-07-01 17:53 KST, cupixvista-api (tesla Rails) 서비스에서 Job 50639의 stop_tasks 흐름이 실행되면서 AwsTask#stop!이 ECS stop_task API를 호출했고, ECS가 InvalidParameterException: The referenced task was not found 응답을 반환했다. DB의 aws_tasks 레코드는 여전히 PENDING 상태였으나 실제 ECS task는 이미 제거되어 있었기 때문에 발생했다. 단일 인스턴스(occurrence_count: 1) 이벤트이며 사용자 요청은 정상 처리되었다.

Quick Facts#

Field Value
exception.class Aws::ECS::Errors::InvalidParameterException
exception.message The referenced task was not found
top_frame app/models/aws_task.rb:172-173
env production, us-west-2
service cupixvista-api (tesla repo, Rails)
affected job Job 50639 (skat-master / create_capture)
affected task AwsTask 49687, ECS task 48ecc8fc146c40e6b7f1da84a352577b
ecs cluster cupix-tesla-ece

Affected Teams#

Team / Domain Error Count Impact
cupixvista-api / Capture pipeline 1 Job 50639은 정상적으로 stopped 상태로 전이되었고 후속 콜백(job_stopped_callback)도 완료. 실제 사용자 영향은 없으며 error 로그만 남았다.

Timeline#

  1. 2026-07-01 10:38:28 KST — AwsTask 49687 created (task_id: 48ecc8fc…, job 50639, task-definition cupix-skat-master-production-arm:1)
  2. 2026-07-01 10:38:30 KST — Task fetched from ECS, last_status: PENDING, desired_status: RUNNING
  3. (간격 ~7h 15m) — 이후 24h 로그 창 내에서 이 task_id에 대한 추가 AwsTask#fetch! 이벤트 없음. ECS 측에서 task가 이미 종료·리핑(reap)된 것으로 추정 (stopped-task-retention 기본 1h)
  4. 2026-07-01 17:53:48 KST — Job 50639 상태가 running → stopped로 전이, stop_tasks 흐름이 StopTaskWorker를 통해 AwsTask#stop! 호출
  5. 2026-07-01 17:53:48 KST — ECS가 InvalidParameterException: The referenced task was not found 반환, error 로그 기록 (본 클러스터의 originating 이벤트)
  6. 2026-07-01 17:53:48 KSTJobCallbackWorkerjob_stopped_callback 정상 완료

Error Log#

Datadog Logs

text
InvalidParameterException on task_id: arn:aws:ecs:us-west-2:619071347432:task/cupix-tesla-ece/48ecc8fc146c40e6b7f1da84a352577b, message: The referenced task was not found

Impact#

  • Service: cupixvista-api
  • 발생 횟수: 1
  • 최초 발생: 2026-07-01 17:53 KST
  • 최근 발생: 2026-07-01 17:53 KST
  • User impact: 없음. Job 50639은 정상적으로 stopped 상태로 전이되었고 job_stopped_callback도 성공적으로 실행됨.

Root Cause Summary#

AwsTask DB 레코드는 여전히 PENDING/RUNNING으로 남아 있었지만, 실제 ECS 상의 task는 이미 종료 및 삭제된 상태였다. Job 50639가 stop 흐름을 타면서 Taskable::Job#stop_tasksStopTaskWorker를 통해 AwsTask#stop!을 호출했고, Aws::ECS::Client#stop_task가 존재하지 않는 task_id에 대해 InvalidParameterException을 던졌다. 이 예외는 이미 stop! 내부에서 rescue되어 error 레벨로 로깅되고 삼켜지는(swallow) 정상적으로 설계된 흐름이며, 실질적인 기능 실패가 아니라 정상적이고 예상 가능한 race 상황의 로그 노이즈이다. DB 상태와 ECS 실제 상태 사이의 불일치는 pull_running/pull_stopping 크론이 이 task를 최근에 pull하지 않아 stale 상태가 유지되었기 때문으로 보인다.

Technical Analysis#

Code Path#

  • Entry point: cron 또는 상태 변경에 의해 Job의 stop_tasks가 호출됨
  • Fan-out: StopTaskWorker.perform_async(task.id)AwsTask#stopAwsTask#stop!
  • Failure point: AwsTask#stop! 내부 ecs_client.stop_task(...) 호출, Aws::ECS::Errors::InvalidParameterException rescue 블록에서 error 로그 발생
app/models/concerns/taskable/job.rb:25-35ruby
def stop_tasks
  aws_tasks.each(&:pull)

  aws_tasks.running.each do |task|
    StopTaskWorker.perform_async(task.id)
  end

  aws_tasks.pending.each do |task|
    StopTaskWorker.perform_async(task.id)
  end
end
  • stop_taskspendingrunning 두 스코프 모두에서 StopTaskWorker로 위임한다. 본 사건의 task 49687은 last_status: PENDING/desired_status: RUNNING이므로 pending 스코프에 해당한다 (aws_task.rb:19).
app/workers/stop_task_worker.rb:1-11ruby
class StopTaskWorker
  include Sidekiq::Worker
  sidekiq_options queue: :default, retry: 1

  def perform(id)
    task = ::AwsTask.find_by_id(id)
    return if task.nil?

    task.stop
  end
end
  • task.stopAwsTask#stop!을 호출하고 예외를 삼킨다(aws_task.rb:190-196).
app/models/aws_task.rb:161-188ruby
def stop!
  begin
    resp = self.ecs_client.stop_task({
      task: task_id,
      cluster: $AWS[:ecs][:cluster_name],
      reason: 'Task has been stopped by Tesla'
    })
  rescue Aws::ECS::Errors::ClientException => e
    Cupix::Logger.error("ClientException on task_id: #{task_id}, message: #{e.message}", class: self.class.name, function: __method__, task: { task_id: task_id })
  rescue Aws::ECS::Errors::ClusterNotFoundException => e
    Cupix::Logger.error("ClusterNotFoundException on task_id: #{task_id}, message: #{e.message}", class: self.class.name, function: __method__, task: { task_id: task_id })
  rescue Aws::ECS::Errors::InvalidParameterException => e
    Cupix::Logger.error("InvalidParameterException on task_id: #{task_id}, message: #{e.message}", class: self.class.name, function: __method__, task: { task_id: task_id })
  rescue Aws::ECS::Errors::ServerException => e
    Cupix::Logger.error("ServerException on task_id: #{task_id}, message: #{e.message}", class: self.class.name, function: __method__, task: { task_id: task_id })
  end

  Cupix::Logger.info(resp, class: self.class.name, function: __method__, task: { task_id: task_id })
  Cupix::Logger.info("Task has been requested to stop on task_id: #{task_id}", class: self.class.name, function: __method__, task: { task_id: task_id })

  return if resp.blank? || resp.task.blank?

  fetched = fetch!(by_force: true)

  unless fetched
    clean_stale_task
  end
end
  • 기대 동작: stop_task가 성공하면 resp.task가 채워지고 이후 fetch!(by_force: true)가 수행되어 DB 상태가 최신화된다.
  • 실제 동작: ECS가 해당 task_id를 찾지 못해 InvalidParameterException을 던지고, rescue 블록이 error 로그를 남긴 뒤 respnil이 되어 return if resp.blank?에서 조기 리턴한다. 이후 fetch!clean_stale_task가 호출되지 않으므로 DB 상의 stale 상태가 그대로 남는다 (다음 pull_pending 크론이 처리해야 함).
  • 참고: fetch!resp.tasks.blank?인 경우 Task not found warn 로그 + last_status: STOPPED 업데이트로 recovery 경로가 존재하는 반면 (aws_task.rb:126-134), stop!은 예외 발생 시 error 로그만 남기고 recovery 경로가 없다.

Log Evidence#

Datadog query used:

text
service:cupixvista-api "48ecc8fc146c40e6b7f1da84a352577b"

핵심 로그(오래된 순, timestamp는 KST):

text
2026-07-01 10:38:28 [info]  [aws_task_stopped] aws_task_model_id: 49687, saved_changes: {"id"=>[nil, 49687], "task_id"=>[nil, "arn:aws:ecs:...48ecc8fc..."], "job_id"=>[nil, 50639], ...}
2026-07-01 10:38:30 [info]  Task fetched: arn:aws:ecs:us-west-2:619071347432:task/cupix-tesla-ece/48ecc8fc... (class=AwsTask, function=fetch!)
2026-07-01 10:38:30 [info]  [aws_task_stopped] aws_task_model_id: 49687, saved_changes: {"last_status"=>[nil, "PENDING"], "desired_status"=>[nil, "RUNNING"], "task_definition_arn"=>[nil, "…cupix-skat-master-production-arm:1"], ...}
2026-07-01 17:53:48 [info]  [Job] state changed from running to stopped on Job 50639
2026-07-01 17:53:48 [error] InvalidParameterException on task_id: arn:aws:ecs:...48ecc8fc..., message: The referenced task was not found (class=AwsTask, function=stop!)
2026-07-01 17:53:48 [info]  Task has been requested to stop on task_id: arn:aws:ecs:...48ecc8fc... (class=AwsTask, function=stop!)
2026-07-01 17:53:48 [info]  job 50639, run job_stopped_callback (class=JobCallbackWorker)
2026-07-01 17:53:48 [info]  job 50639 job_stopped_callback done (class=JobCallbackWorker)

주목할 점:

  1. 10:38:30 이후 17:53:48 이전까지 이 task_id 또는 model_id 49687에 대한 fetch!/batch_pull! 성공/실패 로그가 없다. 즉 pull_pending 크론이 이 특정 task를 최근에 조회하지 않았거나, 조회 결과가 stale이었다.
  2. rescue 블록 실행 이후에도 라인 178-179의 Task has been requested to stop info 로그가 그대로 남는다 (예외 여부와 무관하게 항상 로깅됨). 향후 디버깅 시 오해 소지가 있다.

Datadog cron 로그 검증 쿼리(참고, hit 0):

text
service:cupixvista-api ("clean_running_jobs" OR "@class:Cupix::Cron::Job" OR "TES1000")

→ cron 실행 로그(TES10001…10006)가 24h 창에서 관측되지 않음. Job 50639 stop을 트리거한 정확한 상위 호출자(cron vs 사용자 API vs 다른 콜백)를 24h 로그 창만으로는 특정할 수 없음 — uncertain — needs verification.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 ECS의 실제 task가 이미 종료/리핑되어 있는데 tesla DB의 AwsTask는 stale한 PENDING/RUNNING 상태였고, Job 50639이 stopped 상태로 전이될 때 stop_tasksStopTaskWorkerstop! 흐름이 없는 task에 대해 stop_task API를 호출 (a) 17:53:48 KST에 [Job] state changed from running to stopped on Job 50639 info 로그와 error 로그가 동일 timestamp. (b) 17:53:48 KST의 AwsTask#stop! 호출은 task 49687이 여전히 PENDING이었음을 함의(stop_tasks가 pending scope 포함). (c) ECS 응답 메시지 The referenced task was not found가 존재하지 않는 task임을 명시. (d) 10:38:30 이후 task fetch 로그 부재로 DB 상태가 stale이었을 개연성 높음. Confirmed
H2 잘못된 task_id ARN 형식으로 API 호출 (예: cluster mismatch, region mismatch, ARN corruption) (a) 10:38:30에 동일 task_id로 describe_tasks가 성공(Task fetched: 로그 존재). (b) cluster (cupix-tesla-ece)와 region (us-west-2) 모두 정상 ARN. Rejected
H3 AWS ECS 리전 서비스 자체의 장애 (a) status-board(bun run cli/incident-board.ts for-cluster …) 결과 active/recent incident 없음. (b) 같은 시간대 다른 ECS 관련 error 로그가 관측되지 않음(단건 클러스터, occurrence_count=1). (c) 특정 메시지 "The referenced task was not found"는 outage가 아닌 리소스 부재에서 나오는 응답. Rejected
H4 stop_task 호출 이후 예외를 잘못 rescue하여 후속 로직이 잘못 동작 (a) rescue 이후에도 라인 179 "Task has been requested to stop" info 로그가 unconditionally 남음(오해 소지). (b) resp.blank? guard가 있어 fetch!, clean_stale_task로 진행되지 않음 → 실제 side effect 없음. (c) job_stopped_callback 정상 완료 로그로 흐름 자체는 완결됨. Rejected (as root cause; but see Fix Recommendation §단기 개선)

Fix Recommendation#

즉시 조치 (Critical)#

  • 없음. 사용자 영향이 없고 재발 빈도가 매우 낮은 단건 이벤트다. 로그 노이즈이므로 즉시 코드 변경은 불필요하다.

단기 개선 (1주 이내)#

  • AwsTask#stop!InvalidParameterException 처리 정책 정리 (app/models/aws_task.rb:172-173):
    • "The referenced task was not found" 메시지는 정상적인 race 상황(ECS task가 이미 종료·리핑됨)이며 error가 아니다.
    • 대응 방향: (a) 메시지 패턴 매칭 후 warn 레벨로 다운그레이드하고 (b) fetch!의 recovery 경로(Task not found 처리, aws_task.rb:126-134)와 동일하게 update(last_status: STOPPED, desired_status: STOPPED, stopped_at: Time.current, stopped_reason: 'Task not found (stop!)')로 DB 상태를 정합화한다.
    • 다른 InvalidParameterException 메시지(권한/파라미터 오류)는 계속 error로 유지한다.
  • 오해 소지가 있는 info 로그 정리 (app/models/aws_task.rb:178-179):
    • rescue 블록이 실행된 경우 resp가 nil인데도 "Task has been requested to stop" info 로그가 항상 남는다. resp.present? 조건으로 감싸 실패 시에는 남기지 않는 방향으로 정리한다.
  • 관련 서비스 참고 사항: 서비스 도메인이 cupixvista-api(tesla) — 관련 서비스 노트가 있다면 memory/services/tesla.md 참고.

장기 개선 (재발 방지)#

  • DB ↔ ECS 상태 동기화 강화: pull_pending/pull_running/pull_stopping 크론(lib/cupix/cron/aws_task.rb)의 실행 주기와 실패 시 재시도 정책을 재점검한다. 특정 task가 7시간 넘게 pull되지 않고 stale 상태로 유지된 것은 크론 스케줄 또는 스코프 조건에 갭이 있음을 시사한다 (pending 스코프는 PROVISIONING/PENDING + desired: RUNNING으로 정의되어 있으므로 크론이 계속 pull해야 하는 대상이다).
  • stop!fetch!의 error handling 대칭화: fetch!에서는 "task not found"가 warn + recovery인데 stop!에서는 error + no recovery. 두 메서드가 동일한 예외 클래스와 도메인에 대해 다르게 동작하는 것은 유지보수에 부담이 된다.

Monitoring#

  • Task 관리 error 로그가 급증하는지 감지하기 위한 timeseries widget용 Datadog 쿼리:
text
sum:datadog.estimated_usage.logs.ingested_events{service:cupixvista-api,status:error,@class:AwsTask}.as_count()
  • 위 쿼리는 metric-based로, AwsTask 클래스에서 발생한 error 로그 이벤트 수를 시간 축으로 집계한다. (Note: 정확한 event count 메트릭이 organization에 활성화되어 있어야 함. 미활성화 상태라면 아래 대체 쿼리 사용.)
  • 대체(Log-based metric이 없는 경우) — Datadog Logs Explorer에서 saved view 관리:
text
service:cupixvista-api status:error @class:AwsTask
  • InvalidParameterException만 필터하여 stale-task race 발생률을 추적하려면:
text
service:cupixvista-api "InvalidParameterException" @class:AwsTask "The referenced task was not found"
  • 임계값 제안: 15분당 5건 이상 발생 시 Slack #api-alerts-debug-production 알림 (단기 개선 적용 후 정상적으로는 warn 레벨로 내려가므로 error 카운트는 0에 수렴해야 함).

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial (로그 레벨 조정 + recovery 경로 추가는 국소적 변경)