BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":400,"requestU
RCA: Unknown column 'spacetimes.summary_text' on spacetime summary write
Overview#
What Happened#
capture-intelligence-agent 가 LLM 분석 결과를 spacetime 에 기록하려고 PUT /api/v1/facilities/{key}/spacetimes/{id} 를 호출하면 tesla 가 Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list' 로 실패했다. 2026-08-06 14:06 KST 부터 약 1시간 동안 production 에서만 발생했고, 13 개 facility 의 15 개 spacetime 에 걸쳐 나타났다. tesla 의 production DB 에 spacetimes.summary_text 컬럼이 존재하지 않아 UPDATE 가 거부되면서, 해당 spacetime 의 AI 요약이 저장되지 못하고 summary_state 가 error 로 기록되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Cupix::Errors::Parameter (ARG10001), 원인 Mysql2::Error |
| exception.message | Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list' |
| top_frame | app/repositories/facility_repository.rb:132 (write), :143 (rescue→raise) |
| deploy | tesla production (master), 컬럼 미적용 스키마 |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| whitingturner (tenant cupix) | 29 (tesla 400), 87 (agent 로그) | spacetime 단위 AI 요약 저장 실패, summary_state=error 로 마킹 |
13 개 distinct facility, 15 개 distinct spacetime 에 걸쳐 발생했다. 단일 레코드 재진입이 아니라 스키마 전역 문제다.
Timeline#
- 2026-03-09 22:55 KST — 마이그레이션
20260306071939_add_summary_to_spacetimes가 커밋됨. 파일 버전은20260306071939로 backdate 됨 (8621b4849, TSLA-12069). - 2026-03-09 22:55 KST — 같은 날 상위 버전 마이그레이션
20260309000001_add_summary_state_columns도 커밋됨 (bba57b598). - 2026-07-06 13:31 KST — schema drift 진단 및 수정 커밋
707aac515(TSLA-13490) 이 브랜치origin/jarvis/TSLA-13490에 작성됨. develop/master 에는 미병합. - 2026-08-06 14:06 KST — production 에서 첫
[400] PUT update_spacetime발생 (first_seen). - 2026-08-06 14:51 KST — cluster last_seen.
- 2026-08-06 15:08 KST — 마지막 tesla 400 로그 (06:08:16 UTC). 이후 해당 창에서 관측 종료.
Error Log#
BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":400,"requestUriHref":"http://api-tesla.cupix.internal/api/v1/facilities/rk93lz/spacetimes/1525775?fields%5B0%5D=id&fields%5B1%5D=summary&fields%5B2%5D=summary_state&fields%5B3%5D=facility&fields%5B4%5D=record&fields%5B5%5D=level","bodyResult":{"code":"ARG10001","type":"Cupix::Errors::Parameter","reason":"Invalid argument","message":"Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list'"},"modelId":749925},"sqsMessage":{"MessageId":"92d5fddb-333f-4ada-b7af-f18cddc67e1f","Attributes":{"ApproximateReceiveCount":"1"}}}
Impact#
- Service:
cupixworks-capture-intelligence-agent(원인은 downstreamcupixworks-api/ tesla) - Team: whitingturner
- 발생 횟수: 30 (collector 샘플), 실측 tesla 400 로그 29 건 + agent 로그 87 건 (2026-08-06 14:06 - 15:08 KST 창)
- 최초 발생: 2026-08-06 14:06 KST
- 최근 발생: 2026-08-06 14:51 KST
Root Cause Summary#
tesla production DB 의 spacetimes 테이블에 summary_text 컬럼이 존재하지 않는 schema drift 가 원인이다. summary/summary_text 를 추가하는 마이그레이션 20260306071939_add_summary_to_spacetimes 는 2026-03-09 에 커밋되면서 파일 버전이 20260306071939 로 backdate 되었다. Rails 는 schema_migrations 의 max 버전보다 큰 마이그레이션만 실행하므로, 같은 날 커밋된 상위 버전 20260309000001 을 먼저 적용한 환경은 backdate 된 20260306071939 를 영구히 건너뛰었다. 그 결과 이 환경들은 summary/summary_state 는 있지만 summary_text 는 없는 상태가 되었고, db/schema.rb 는 재생성되어 summary_text 를 포함하므로 코드 리뷰에서는 드러나지 않았다. capture-intelligence-agent 가 요약을 기록할 때 tesla update_spacetime 이 spacetime.summary_text = spacetime.summary_text_for_search 를 세팅하고 save! 하면서 없는 컬럼에 UPDATE 를 발행 → Mysql2::Error: Unknown column → rescue StandardError 가 ARG10001 로 재포장 → HTTP 400. 이 drift 를 고치는 idempotent 재적용 마이그레이션(TSLA-13490)은 브랜치 origin/jarvis/TSLA-13490 에만 있고 develop/master 에 병합되지 않아 production 에 배포된 적이 없다.
Technical Analysis#
Code Path#
Entry point: agent 의 요약 완료 write.
logger.info('CaptureIntelligenceService::updateSpacetimeSummary | spacetimeId: %d, summary: %s',
spacetimeId, describeSummary(summary));
await this.cupixApi.spacetime.update(facilityKey, spacetimeId, {
summary,
summary_state: state
});
summary 가 함께 전달되므로 tesla update_spacetime 의 summary 분기가 실행된다.
if params[:summary].present?
summary = params[:summary].is_a?(ActionController::Parameters) ? params[:summary].permit!.to_h : params[:summary]
spacetime.validate_summary!(summary)
spacetime.summary = summary
spacetime.summary_text = spacetime.summary_text_for_search
end
spacetime.summary_state = params[:summary_state] if params.key?(:summary_state)
Failure point: save! 가 없는 컬럼에 UPDATE 를 발행하고, generic rescue 가 이를 400 으로 재포장한다.
begin
spacetime.save!
rescue StandardError => e
raise Cupix::Errors::Parameter.new(code: 'ARG10001', reason: 'Invalid argument', message: e.message)
end
기대 동작: summary_text 컬럼에 검색용 평문을 저장하고 200 을 반환한다. 실제 동작: production DB 에 컬럼이 없어 Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list' 가 발생하고, 400 (ARG10001) 으로 응답한다.
drift 의 근거는 스키마 정의와 실제 적용의 불일치다. master 의 db/schema.rb 에는 spacetimes 블록에 summary_text 가 없지만, 컬럼을 추가하는 마이그레이션 파일은 존재한다.
class AddSummaryToSpacetimes < ActiveRecord::Migration[7.2]
def change
change_table :spacetimes, bulk: true do |t|
t.json :summary
t.text :summary_text
end
end
end
TSLA-13490 이 작성한 수정 마이그레이션은 이 backdate 메커니즘을 문서화하고 idempotent 하게 재적용한다. 다만 이 파일은 develop/master 에 병합되지 않았다.
class ReaddSummaryColumnsToSpacetimes < ActiveRecord::Migration[7.2]
def change
change_table :spacetimes, bulk: true do |t|
t.json :summary unless column_exists?(:spacetimes, :summary)
t.text :summary_text unless column_exists?(:spacetimes, :summary_text)
end
end
end
agent 쪽은 이 400 을 수신해 로깅하고 SQS 메시지를 삭제한 뒤 updateErrorState 로 summary_state 를 error 로 기록한다 (base-service.ts:300-311, capture-intelligence-service.ts:198-204). 즉 요약 결과가 폐기된다.
Log Evidence#
tesla 400 요청 로그 쿼리:
service:cupixworks-api "spacetimes.summary_text"
결과: 29 건, 100% production, 100% PUT ... #update_spacetime. GET (show_spacetime) 에서는 0 건.
[400] PUT /api/v1/facilities/rk93lz/spacetimes/1525775 (Api::V1::FacilitiesController#update_spacetime)
[400] PUT /api/v1/facilities/m30gmo/spacetimes/1524911 (Api::V1::FacilitiesController#update_spacetime)
전체 언급 (agent + api) 쿼리:
"Unknown column 'spacetimes.summary_text'"
결과: 116 건 (agent 87 + api 29), 100% production, 창 2026-08-06 05:06:14 UTC - 06:08:16 UTC (14:06 - 15:08 KST).
agent 쪽 원문 (warn 단계, 400 수신 시점):
CupixAuth::handleError | Response statusCode: 400, requestUriHref: http://api-tesla.cupix.internal/api/v1/facilities/rk93lz/spacetimes/1525775?fields%5B0%5D=id&fields%5B1%5D=summary&fields%5B2%5D=summary_state&fields%5B3...
captures.summary_text 는 동일 창에서 0 건이었다. captures 테이블은 컬럼이 정상 적용되어 있고, spacetimes 만 drift 되었음을 확인한다.
service:cupixworks-api "captures.summary_text"
영향 범위: 13 개 distinct facility (rk93lz, m30gmo, yxhl8x, ywdtr, fgxxfo, psec02, 5ivznb, 2oaypp, hox1e3, umphr6 등), 15 개 distinct spacetime (1525775, 1524911, 1525824, ...). 특정 레코드 재진입이 아닌 스키마 전역 결정론적 실패다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | production spacetimes 에 summary_text 컬럼이 없는 schema drift (backdate 된 마이그레이션이 skip 됨) |
master db/schema.rb 의 spacetimes 블록에 summary_text 없음 (grep count 0), 마이그레이션 20260306071939 는 존재하나 파일 버전이 커밋일(2026-03-09)보다 backdate 됨; TSLA-13490 커밋 메시지가 동일 메커니즘 진단; 13 facility/15 spacetime 전역 결정론적 실패 |
— | Confirmed |
| H2 | 배포 cutover 중 stale schema cache 로 인한 transient 오류 (ispring_user_id 4b1f8e9b 유형) |
05:xx UTC 시간창 집중, ispring 버스트와 동일 창 | 이 케이스는 destructive migration(컬럼 삭제)이 아니라 additive migration 이 아예 적용 안 됨; TSLA-13490 이 "dev-only schema drift, not transient" 로 명시; captures 는 정상이고 spacetimes 만 실패 = 캐시 문제라면 이런 선택적 실패 안 나옴 | Rejected |
| H3 | 애플리케이션 코드가 잘못된 컬럼을 참조 (코드 결함) | Unknown column 메시지 |
summary_text 는 summary_text_for_search/검색 필터에서 정당하게 참조됨 (facility_repository.rb:69,132, clearable/spacetime.rb:11); develop db/schema.rb:4126 에 컬럼 정의 존재; 코드는 스키마 정의와 일치 |
Rejected |
| H4 | 이 클러스터가 동일 창의 ispring_user_id(ET acdd2caa) / users/records Unknown column 버스트와 같은 이슈 |
같은 2026-08-06 05:xx UTC 창, 같은 Unknown column ... field list 형태 |
컬럼/테이블/서비스 상이 (spacetimes vs users vs records), Datadog ET 는 컬럼별 별개 이슈로 집계; captures.summary_text 는 0 건 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- schema drift 수정 마이그레이션을 develop/master 에 병합하고 production 에 배포한다. 이미 작성된
db/migrate/20260613000000_readd_summary_columns_to_spacetimes.rb(브랜치origin/jarvis/TSLA-13490, TSLA-13490) 가column_exists?가드로 idempotent 하게summary/summary_text를 재적용한다. 컬럼이 있는 환경에서는 no-op, drift 된 production DB 에서는 컬럼을 복구한다. 신규 코드 작성 없이 기존 PR 병합으로 해소된다. - 병합 전 확인:
git merge-base --is-ancestor 707aac515 origin/develop이 false 이므로 (미병합) 별도 신규 마이그레이션을 만들지 말고 이 PR 을 병합한다.
단기 개선 (1주 이내)#
update_spacetime의rescue StandardError => e(facility_repository.rb:143) 는 스키마 오류 같은 서버측 결함까지ARG10001(client 400) 으로 분류한다.Mysql2::Error/ActiveRecord::StatementInvalid는 400 이 아닌 500 로 매핑해 client 오류로 오분류되지 않도록 rescue 를 좁힌다. 그래야 이런 스키마 결함이 client 입력 오류로 위장되지 않고 알람에 노출된다.
장기 개선 (재발 방지)#
- 배포 파이프라인에
db/schema.rb와 실제 DB 컬럼 정합성 검증 스텝을 추가한다. drift 의 근본 원인은 backdate 된 마이그레이션 버전이므로, 마이그레이션 파일 버전이 커밋 시각보다 과거인 경우 CI 에서 경고하도록 lint 를 도입한다. - capture-intelligence-agent 가 요약 write 실패 시
summary_state=error로 결과를 폐기하기 전에, 서버측 5xx/스키마성 오류와 진짜 client 입력 오류를 구분해 재시도 여부를 결정하도록 한다 (현재는 4xx 를 모두 삭제 대상으로 처리).
Monitoring#
drift 재발 또는 미해소를 추적하는 로그 검색 쿼리다. 수정 배포 후 0 으로 떨어져야 한다.
service:cupixworks-api status:error "spacetimes.summary_text"
agent 측 요약 write 실패 관측:
service:cupixworks-capture-intelligence-agent "Unknown column 'spacetimes.summary_text'"
update_spacetime 400 전반 (요약 저장 경로 건강도):
service:cupixworks-api @http.status_code:400 "update_spacetime"
Risk Assessment#
- Risk level: medium (production 사용자 대상 AI 요약 저장 실패, 데이터 손실은 없으나 요약 결과 폐기)
- 예상 복잡도: trivial (기존 idempotent 마이그레이션 병합/배포)