invoke postprocessor failed on Capture 47027
RCA: invoke postprocessor failed on Capture 47027
Overview#
What Happened#
2026-07-14 17:12 KST에 cupixworks-worker (tesla, ap-southeast-2) 프로세스가 6개의 오래된 singleshot capture(47027, 47029, 47032, 47035, 47036, 47037)에 대해 postprocessor 재실행을 시도했으나 모두 NoMethodError: undefined method 'actions' for nil 로 실패했다. 6건 모두 동일한 초에 동일 pid(712734)에서 발생했고, 로그 tag function: reinvoke_post 는 tesla 정규 코드 경로에는 존재하지 않는 이름이라 rails console/rake 등 ad-hoc 스크립트가 원인으로 판단된다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | NoMethodError (Ruby, 메시지 텍스트로 추정) |
| exception.message | undefined method \actions' for nil` |
| top_frame | 로그에 stack trace 미포함 — uncertain (needs verification) |
| runtime | Ruby / Rails (log/tesla_production-json.log) |
| deploy | production-ap-southeast-2-20260714T0808Z0-937582c6-cupixworks |
| env | production, region ap-southeast-2 (prod-au), tenant cupix |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
Built Holdings Pty Ltd (team.id: 16, domain built) |
6 | 이미 처리 완료(state: done)된 과거 singleshot capture 6건에 대한 재-postprocess 시도 실패. 사용자 트래픽/신규 처리에는 영향 없음. |
Timeline#
- 2025-08-17 08:26 KST — Capture 47027 등 원본 캡처가 촬영됨(
captured_at: 2025-08-17T21:26:10ZUTC → 2025-08-18 06:26 KST). 이후state: done,singleshot_state: stopped로 처리 완료. - 2026-07-14 17:08 KST —
937582c6리비전 배포 완료(production-ap-southeast-2-20260714T0808Z0). - 2026-07-14 17:12 KST — ad-hoc 스크립트가 6개 capture에 대해
invoke postprocessor on Capture Ninfo 로그를 emit → 즉시invoke postprocessor failed on Capture Nerror 로그로 실패. 모두 동일 pid712734, 동일 hostnameip-10-1-19-158.ap-southeast-2.compute.internal.
Error Log#
invoke postprocessor failed on Capture 47027
같은 pid에서 대응되는 info 로그 (직전 발생):
{
"timestamp": "2026-07-14T08:12:43.881Z",
"status": "info",
"message": "invoke postprocessor on Capture 47027",
"function": "reinvoke_post",
"pid": 712734,
"host": "ip-10-1-19-158.ap-southeast-2.compute.internal"
}
error 로그의 attributes:
{
"timestamp": "2026-07-14T08:12:43.882Z",
"status": "error",
"message": "invoke postprocessor failed on Capture 47027",
"function": "reinvoke_post",
"error": { "msg": "undefined method `actions' for nil" },
"pid": 712734
}
Impact#
- Service:
cupixworks-worker - 발생 횟수: 6
- 최초 발생: 2026-07-14 17:12 KST
- 최근 발생: 2026-07-14 17:12 KST
- 영향 범위: ad-hoc 스크립트의 실패 6건. 유저 요청·정기 처리 파이프라인에는 영향 없음.
Root Cause Summary#
function: reinvoke_post 태그를 가진 6건의 로그는 tesla develop/master 및 배포된 커밋 937582c6 어디에서도 grep 되지 않는다. 즉 tesla 정규 코드 경로가 아니라 rails console 또는 rake 태스크에서 실행된 ad-hoc 스크립트가 emit 한 로그다. 이 스크립트는 대상 capture 목록에 대해 invoke postprocessor on Capture #{id} 를 로깅한 뒤 job/action 을 획득해 postprocessor 를 재실행하려 했다. 그러나 대상 6건은 모두 singleshot capture (method: singleshot, capture_type: app_no_ar_singleshot) 로 일반 처리 파이프라인이 만드는 create_capture / create_pano_postprocessor Job 이 존재하지 않는다. 스크립트가 capture.jobs.<scope>.last.actions.<...> 형태로 접근하면서 <scope>.last 가 nil 을 반환했고, 뒤이은 .actions 호출에서 undefined method 'actions' for nil 이 발생했다. 즉 근본 원인은 스크립트의 capture 종류(singleshot) 미검증 + Job 존재 nil-guard 부재이며, tesla 서비스 코드의 버그는 아니다.
Technical Analysis#
Code Path#
reinvoke_post 심볼과 "invoke postprocessor on Capture" 문자열이 tesla 리포지토리 전 refs(로컬/원격 브랜치, 937582c6 배포 커밋 포함)에서 grep 되지 않음을 확인했다. 따라서 실행된 코드는 리포지토리에 커밋된 파일이 아니며, rails console 세션 또는 서버 로컬에 존재하는 임시 스크립트로 추정된다. 동일 pid 로그 시퀀스가 아래와 같이 정규 서비스 코드(Capture#run_postprocessor_agent) 를 호출한다는 사실이 이를 뒷받침한다.
Job 존재 여부와 무관하게 반복적으로 참조되는 .actions collection 은 다음 파일들에서 확인된다 — 스크립트가 이 중 하나의 패턴을 사용했을 가능성이 크다:
def run_last_action
last_job = self.jobs.last
last_job.previous_error_code = last_job.error_code
last_job.previous_processing_status = last_job.processing_status
last_job.processing_status = nil
last_job.error_code = nil
last_job.create_actions! unless last_job.actions.exists?
last_action = last_job.actions.last
last_action.reset_state!
last_job.running_state! unless last_action.command.name == 'preprocessor'
last_action.run_action
end
singleshot capture 는 정규 흐름에서 self.jobs 에 create_capture 계열 Job 을 만들지 않고 process_singleshot! 만 호출하는 경로가 있다:
def invoke_capture
Cupix::Logger.info("invoke capture processing for capture #{self.id}.", ...)
self.processing_required = false
if singleshot?
process_singleshot!
elsif json_upload? || migrated_from_einstein_v2? || reality_capture?
run_pano_postprocessor
...
또한 정규 postprocessor 트리거는 다음과 같이 job 을 명시적으로 얻은 후 사용한다. 이 함수 이름(run_postprocessor_agent) 은 실패한 capture 와 대비되는 성공한 capture 로그(예: 81919/81920/81968) 에도 그대로 등장한다:
def run_postprocessor_agent(job, action_name = nil)
session = self.user.agent_team_session(self.team)
client = Cws::CapturePostprocessorAgent::Client.new(model: self, job: job, session: session, action_name: action_name)
client.invoke!
Cupix::Logger.info("postprocessor agent is invoked for capture #{self.id} job id: #{job.id}", class: self.class.name, function: __method__, ...)
end
.actions 컬렉션을 참조하는 다른 대표 지점들 (스크립트가 재실행 시퀀스를 구성하며 사용했을 가능성이 있음):
preprocessor_action = capture_job.actions.joins(:command).find_by("commands.name = 'preprocessor'")
...
skat_action = capture_job.actions.joins(:command).find_by("commands.name = 'skat-master'")
...
postprocessor_action = capture_job.actions.joins(:command).find_by("commands.name = 'postprocessor'")
기대 동작 vs 실제 동작
- 기대: 스크립트가 대상 capture 를 필터링(예:
capture.method != 'singleshot'또는capture.jobs.exists?) 하거나, job/action 조회 결과에 대해 nil-guard 를 두어 skip 로깅 후 다음으로 이동. - 실제: 필터링/nil-guard 부재로 singleshot capture 6건에 대해
nil.actions호출 →NoMethodError발생.
Log Evidence#
Datadog 쿼리 (스크립트 실행 pid 로 좁힌 창):
service:cupixworks-worker @pid:712734
성공한 capture 의 로그 시퀀스는 다음과 같이 run_postprocessor_agent (class: Capture) 로 이어진다 — 스크립트가 postprocessor 재기동을 정상적으로 수행한 사례:
{ "timestamp": "2026-07-14 17:14:34", "status": "info",
"message": "invoke postprocessor on Capture 81968",
"function": "reinvoke_post" }
{ "timestamp": "2026-07-14 17:14:34", "status": "info",
"message": "postprocessor agent is invoked for capture 81968 job id: 226061",
"class": "Capture", "function": "run_postprocessor_agent" }
{ "timestamp": "2026-07-14 17:14:34", "status": "info",
"message": "invoke job_running_callback with jid: true for job 226061" }
{ "timestamp": "2026-07-14 17:14:34", "status": "info",
"message": "[Capture] state changed from done to processing",
"function": "log_event" }
실패한 6건 의 로그는 성공 시퀀스의 postprocessor agent is invoked ... 라인 없이 곧바로 error 로 종결된다:
Datadog 쿼리: service:cupixworks-worker @function:reinvoke_post status:error
Time range: now-14d
- 2026-07-14 17:12:43 Capture 47027 — undefined method `actions' for nil
- 2026-07-14 17:12:43 Capture 47029 — undefined method `actions' for nil
- 2026-07-14 17:12:43 Capture 47032 — undefined method `actions' for nil
- 2026-07-14 17:12:43 Capture 47035 — undefined method `actions' for nil
- 2026-07-14 17:12:43 Capture 47036 — undefined method `actions' for nil
- 2026-07-14 17:12:43 Capture 47037 — undefined method `actions' for nil
Kibana(prod-au) 로 실패한 capture 들의 상태를 확인한 결과 6건 모두 singleshot 이고 성공한 대상들은 non-singleshot 이다:
| capture_id | method | capture_type | state | singleshot_state | captured_at | 결과 |
|---|---|---|---|---|---|---|
| 47027 | singleshot | app_no_ar_singleshot |
done | stopped | 2025-08-17 | 실패 |
| 47029 | singleshot | app_no_ar_singleshot |
done | stopped | 2025-08-17 | 실패 |
| 47032 | singleshot | app_no_ar_singleshot |
done | stopped | 2025-08-17 | 실패 |
| 47035 | singleshot | app_no_ar_singleshot |
done | stopped | 2025-08-17 | 실패 |
| 47036 | singleshot | app_no_ar_singleshot |
done | stopped | 2025-08-17 | 실패 |
| 47037 | singleshot | app_no_ar_singleshot |
done | stopped | 2025-08-17 | 실패 |
| 81919 | (non-singleshot) | null | done | none | (recent) | 성공 |
| 81920 | (non-singleshot) | null | done | none | (recent) | 성공 |
| 81968 | (non-singleshot) | null | processing | none | (recent) | 성공 |
또한 실패 로그에는 class (또는 class_name) attribute 가 비어 있는 반면, tesla 정규 코드의 Cupix::Logger.info(...) 호출은 예외 없이 class: 를 지정한다. 이는 스크립트가 리포지토리 컨벤션을 따르지 않는 ad-hoc 코드임을 강하게 시사한다.
git grep 결과 (evidence for "not in repo"):
$ git grep -in "invoke postprocessor" 937582c6 -- '*.rb'
(no matches)
$ git grep -in "reinvoke_post" 937582c6 -- '*.rb'
(no matches)
$ git log --all --format='%H %s' -S 'invoke postprocessor'
(no matches)
$ git log --all --format='%H %s' -S 'reinvoke_post'
(no matches)
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | ad-hoc 스크립트(rails console/rake)가 singleshot capture 에 대해 job/action 을 nil-guard 없이 재실행하려다 실패 | (1) reinvoke_post·"invoke postprocessor on/failed on Capture" 문자열이 tesla 전 refs 및 배포 커밋 937582c6 grep 결과 0건. (2) 실패 6건 모두 method: singleshot. 성공 사례(81919/81920/81968)는 non-singleshot. (3) 실패 로그에 class attribute 부재 — 정규 Cupix::Logger 사용 패턴과 불일치. (4) 6건 모두 동일 pid, 동일 초에 발생 — 일회성 배치. |
스크립트 원본 위치를 서버 접근 없이 확인 불가 (uncertain — needs verification via SSH/console history). | Confirmed |
| H2 | TSLA-13272 AutoReprocessCaptureWorker 가 원인 |
최근 자동 재처리 기능이 merge 됨. | (1) worker code (app/workers/auto_reprocess_capture_worker.rb) 는 로그 문자열 "[AutoReprocess] triggered" 를 사용하며 실패 로그 문자열과 무관. (2) capture.auto_reprocessable? 가 false 인 경우 즉시 return. (3) worker 는 @class:AutoReprocessCaptureWorker 태그가 붙어야 하는데 관측된 error 로그에는 class 없음. |
Rejected |
| H3 | tesla Refinementable#run_refinement_postprocessor 의 _job = model.jobs.where(kind: 'create_capture').last 가 nil 이라 발생 |
.actions 참조를 하는 유사 경로 존재. |
(1) 해당 메서드는 _job.blank? 시 로그 "job not found. failed to run postprocessor" 를 남기고 early return — 관측된 에러 텍스트와 불일치. (2) .actions 를 호출하지 않음. |
Rejected |
| H4 | 외부 dependency(SQS/DB) 장애 | status-board에 svc:cupixworks-worker::unknown 최근 인시던트 있음. | (1) 이 클러스터의 active 는 null. (2) 성공/실패 로그가 동일 pid·동일 초에 혼재 — dependency 장애가 아니라 데이터 종속적 실패. (3) 에러 메시지가 NoMethodError 로 net/http 계열 예외가 아님. |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 없음. tesla 서비스 코드 결함이 아니므로 자동 code-fix 대상 아님. 스크립트 실행자에게 다음 사실 전달 필요:
- 대상 6건은 singleshot capture 로 postprocessor 재기동 대상이 아니며, 스크립트가 singleshot 을 필터링해야 한다.
- 추가로 job/action lookup 결과에 대한 nil-guard 를 두어야 한다.
- 대상 capture 목록에 원치 않는 항목이 섞였는지 재검토 필요.
단기 개선 (1주 이내)#
- ad-hoc 스크립트를 리포지토리로 이관: 재사용 가능성이 있는 postprocessor 재기동 로직이라면
lib/tasks/tesla/또는app/services/하위의 정식 rake task/service 로 커밋해 코드 리뷰·CI 를 거치도록 한다. 정규 파일이 되면 이번처럼 조사 시 grep 으로 즉시 위치 파악이 가능해진다. - 필터 명시: 스크립트/신규 task 에
next unless capture.reprocessible?또는 최소한next if capture.singleshot?/next if capture.jobs.where(kind: %w[create_capture create_pano_postprocessor]).blank?조건을 넣는다. - 로거 컨벤션 준수:
Cupix::Logger.info(msg, class: self.class.name, function: __method__, capture: { id: capture.id })형식으로class·function·컨텍스트 attribute 를 반드시 지정. 이번 클러스터는class부재 때문에 코드 위치 추적이 크게 지연됐다.
장기 개선 (재발 방지)#
- 재처리 진입점 통일: capture 재계산 경로를 (a) 사용자 트리거(
CaptureInvoker#reprocess_capture), (b) 자동 재처리(TSLA-13272AutoReprocessCaptureWorker), (c) 운영 도구 세 가지로 명확히 정리하고, 운영 도구도 (b)와 동일한Capture#auto_reprocessable?판정을 재사용하도록 강제한다. - console 사용 감사: production rails console 에서 실행되는 대량 mutation 스크립트는 사전 승인/기록이 되도록 프로세스를 정비한다 (예: 실행 파일을 PR 로 만들어 병합 후 SSH 로 실행).
Monitoring#
- 스크립트가 재실행될 가능성이 있으므로 동일 시그니처를 지속 감시.
service:cupixworks-worker status:error @error.msg:"undefined method \`actions' for nil"
- singleshot capture 를 대상으로 잘못된 재계산 트리거가 있는지 감시:
service:cupixworks-worker status:error "invoke postprocessor failed on Capture"
reinvoke_post라는 tag 자체가 정규 코드가 아니므로 등장 여부만으로 알람 가능:
service:cupixworks-worker @function:reinvoke_post status:error
Risk Assessment#
- Risk level: low — 사용자 영향 없음, tesla 코드 변경 불필요, 일회성 batch 실패.
- 예상 복잡도: trivial — 스크립트 실행자에게 필터/가드 추가 안내 및 (선택) 스크립트 리포지토리 이관.