ES /docs

Cupix::Errors::Argument: No mapping found for [id] in order to sort on

RCA: Cupix::Errors::Argument: No mapping found for [id] in order to sort on

Overview#

What Happened#

cupixworks-api (tesla) 의 목록 조회 API (GET /api/v1/panos, GET /api/v1/floorplans 등) 에 클라이언트가 인덱스 매핑에 존재하지 않는 필드로 정렬을 요청(?sort=id, ?order_by=revision, ?sort_order_by=updated_at desc 등)하면, tesla 가 그 값을 검증 없이 Elasticsearch sort 절에 그대로 전달한다. ES 는 query_shard_exception No mapping found for [X] in order to sort on ([400]) 을 반환하고, tesla 는 이를 [502] BG10002 Bad Gateway on Elasticsearch 로 클라이언트에 응답한다. 즉 클라이언트 입력 오류(4xx 성격)가 서버 게이트웨이 오류(5xx)로 잘못 매핑되어 Error Tracking 에 집계된 것으로, 서버 로직 결함은 아니다.

Quick Facts#

Field Value
exception.class Cupix::Errors::Argument (representative) / 실제 응답은 Cupix::Errors::BadGateway BG10002
exception.message No mapping found for [id] in order to sort on
top_frame app/controllers/concerns/searchable_controller.rb:399-402 (build_sort_query)
runtime Ruby on Rails (tesla monorepo), Elasticsearch 7.5
env production (multi-region)

Affected Teams#

Team / Domain Error Count Impact
cupixworks-api (tesla) — 목록 조회 API retention 창 8건 (누적 21) 정렬 파라미터에 잘못된 필드를 보낸 요청이 502 응답. 정상 파라미터 요청에는 영향 없음

Timeline#

  1. 2025-01-23 14:01 KST — 최초 발생 (first_seen)
  2. 2026-07-20 16:01 KST — 클러스터 last_seen (Error Tracking 집계 기준)
  3. 2026-07-27 09:18 KST — retention 창 내 실제 로그 확인 ([502] GET /api/v1/panos, sort field [-id]/[id desc]/[id])
  4. 2026-07-27 15:10~17:56 KST[502] GET /api/v1/floorplans, sort field [revision] 반복
  5. 2026-08-04 — RCA 수행

Error Log#

Datadog Logs

text
No mapping found for [id] in order to sort on

Impact#

  • Service: cupixworks-api
  • 발생 횟수: 21
  • 최초 발생: 2025-01-23 14:01 KST
  • 최근 발생: 2026-07-20 16:01 KST

Root Cause Summary#

목록 조회 컨트롤러의 정렬 파라미터 처리(SearchableController#build_sort_query, searchable_controller.rb:399-402)가 클라이언트가 보낸 order_by 값을 SORTABLE_FIELDS_ALIAS(5개: name/creator/user/rank/group_type) 에 없으면 order_by.to_sym 으로 그대로 Elasticsearch sort 절에 넣는다. 인덱스 매핑 검증이 없어, ES 가 인식하지 못하는 정렬 키를 그대로 받으면 query_shard_exception No mapping found for [X] in order to sort on ([400]) 를 반환한다. tesla 는 이 Elasticsearch::Transport::Transport::Errors::BadRequestserver_error_controller.rb:12-13rescue_from Elasticsearch::Transport::Transport::Error → badgateway_on_elasticsearch_502_error 로 잡아 HTTP 502 BG10002 로 응답한다.

관측된 실패 정렬 키는 두 부류다(retention 창 8건 전수 분석, 상세는 아래 Log Evidence 표): (1) 매핑은 존재하지만 필드명이 malformedpanos-id, id desc, updated_at desc (id·updated_atpanos/floorplans 양쪽에 정렬 가능 필드로 매핑돼 있으나, 클라이언트가 방향/부호를 sort 파라미터로 분리하지 않고 order_by 값 안에 섞어 넣어 "-id"/"id desc" 라는 존재하지 않는 필드명이 됨); (2) 필드 자체가 인덱스 매핑에 없음floorplansrevision (Searchable::Floorplan 매핑에 정의 없음, dynamic 매핑도 해당 필드를 담은 문서가 없어 미생성). 어느 쪽이든 근본 원인은 클라이언트의 정렬 파라미터 입력 오류이며, 유일한 결함은 이를 5xx(502 BG10002)로 표면화하는 status-code 매핑이다 — 서버 로직/데이터 결함이 아니다.

Technical Analysis#

Code Path#

  • Entry point: app/controllers/api/v1/panos_controller.rb:16 (Api::V1::PanosController#index), 동일 패턴이 FloorplansController#index 등에 적용
  • 정렬 파라미터 조립: SearchableController#get_query_optionorder_bybuild_sort_query
  • Failure point: app/controllers/concerns/searchable_controller.rb:399-402 — 화이트리스트 밖 필드를 검증 없이 ES sort 절에 전달
  • Status-code 매핑: app/controllers/concerns/server_error_controller.rb:12-13,46-48 — ES BadRequest → 502 BG10002

컨트롤러 진입:

app/controllers/api/v1/panos_controller.rb:16-27ruby
def index
  pano_query_option = Cupix::QueryOption::Pano.new(get_query_option(enable_current_team: false), params)
  panos = repository_instance.search(pano_query_option)
  # ... render
end

정렬 파라미터를 그대로 ES sort 키로 사용 (매핑 검증 없음):

app/controllers/concerns/searchable_controller.rb:399-402ruby
def build_sort_query(sort, order_by)
  sort_key = SORTABLE_FIELDS_ALIAS.include?(order_by) ? SORTABLE_FIELDS_ALIAS[order_by] : order_by.to_sym
  { sort_key => { order: sort.to_sym, missing: '_last' } }
end

기대 동작: 정렬 가능한 필드(화이트리스트 또는 인덱스 매핑에 존재하는 필드)만 sort 절에 반영하고, 그 외에는 클라이언트 4xx(ARG10001 등)로 거부. 실제 동작: SORTABLE_FIELDS_ALIAS(5개) 에 없는 임의 필드(id, revision, updated_at)를 to_sym 으로 통과시켜 ES 가 매핑 오류를 던짐.

ES BadRequest 를 502 로 매핑:

app/controllers/concerns/server_error_controller.rb:12-13,46-48ruby
rescue_from Elasticsearch::Transport::Transport::Error,
            Faraday::ConnectionFailed, with: :badgateway_on_elasticsearch_502_error
# ...
def badgateway_on_elasticsearch_502_error(exception)
  raise_error(502, exception, code: 'BG10002', type: Cupix::Errors::BadGateway, reason: 'BadGateway on Elasticsearch', message: exception.message)
end

참고 — repository 에도 ES BadRequest → ARG13000 rescue 가 존재하지만, 실제 관측 응답은 502 BG10002 이고 로그에 raw ES 메시지가 그대로 실려 있어(아래 Log Evidence) 예외가 controller 의 rescue_from Elasticsearch::Transport::Transport::Error 로 도달했음을 확인:

app/repositories/base_repository.rb:83-97ruby
rescue Elasticsearch::Transport::Transport::Errors::BadRequest => e
  _message = JSON.parse(e.message.split(/\[\d{3}\]\s/i)[1]) rescue {}
  _reason = _message['error']['root_cause'][0]['reason'] rescue nil
  raise Cupix::Errors::Argument.new(code: 'ARG13000', reason: _reason)
# ...
rescue StandardError => e
  Cupix::Logger.error(e.message.to_s, class: self.class.name, method: __method__)
  raise Cupix::Errors::BadGateway.new(code: 'BG10002', reason: 'Bad Gateway error on Elasticsearch')
end

Log Evidence#

Datadog 쿼리 (재현):

text
service:cupixworks-api "in order to sort on"
text
service:cupixworks-api "No mapping found"

retention 창(now-14d, 기준 2026-08-04) 에서 8건 확인. 모두 status:info 요청 로그이며 응답코드 [502], error.message 필드에 raw ES 예외가 담김:

json
{
  "timestamp": "2026-07-27T00:18:43.484Z",
  "message": "[502] GET /api/v1/panos (Api::V1::PanosController#index)",
  "http": { "status_code": 502, "method": "GET", "url_details": { "path": "/api/v1/panos" } },
  "error": {
    "message": "[400] {\"error\":{\"root_cause\":[{\"type\":\"query_shard_exception\",\"reason\":\"No mapping found for [-id] in order to sort on\",\"index\":\"panos\"}],\"type\":\"search_phase_execution_exception\",\"reason\":\"all shards failed\"}}"
  }
}

정렬 필드 변형(같은 이슈로 묶임): panos 인덱스 — [-id], [id desc], [updated_at desc], [id]; floorplans 인덱스 — [revision]. Representative [id] 는 현재도 발생하는 변형 중 하나로 stale 이 아니지만, Error Tracking 이 여러 sort-field 변형을 하나의 이슈로 묶고 있음.

인덱스별 "무엇이 빠졌는가" 전수 분석 (revision #1)#

retention 창(now-14d, 기준 2026-08-06) 8건 전부의 attributes.params 를 로그에서 추출해, 어떤 인덱스에 어떤 정렬 필드가 요청됐고 그것이 매핑에 존재하는지 판정:

인덱스 컨트롤러 클라이언트 order_by (원본) ES 로 넘어간 sort key 그 필드가 인덱스 매핑에 있나? 실패 원인 분류 건수
panos PanosController#index -id -id id 는 매핑 O (searchable/pano.rb:34 integer), 그러나 -id 는 아님 malformed field name (- prefix) 1
panos PanosController#index id desc id desc id 는 매핑 O, 그러나 id desc 는 아님 malformed field name (방향이 필드명에 포함) 1
panos PanosController#index updated_at desc updated_at desc updated_at 은 매핑 O (searchable/pano.rb:97 date), 그러나 updated_at desc 는 아님 malformed field name (방향이 필드명에 포함) 1
floorplans FloorplansController#index revision revision revision 은 매핑 X (searchable/floorplan.rb 에 정의 없음) genuinely unmapped field 5

즉, 정렬 실패는 두 부류로 나뉜다:

  1. 매핑은 존재하나 필드명이 malformed (panos: -id, id desc, updated_at desc). id·updated_at 은 두 인덱스 모두에 정렬 가능 필드로 매핑돼 있다. 실패한 이유는 클라이언트가 방향/부호를 별도 sort 파라미터로 넘기지 않고 order_by 값 안에 섞어 넣었기 때문(?order_by=-id, ?order_by=id desc). build_sort_query 는 이 문자열 전체를 to_sym 해 sort key 로 쓰므로(searchable_controller.rb:400), ES 인덱스에 "-id"/"id desc" 라는 이름의 필드가 없어 query_shard_exception 이 난다. tesla 가 지원하는 문법은 ?order_by=id&sort=desc (또는 ?sort_order_by=id desc, 이 경우 build_multi_sort_query 가 공백으로 split 함, searchable_controller.rb:374-383) 이다.
  2. 필드 자체가 인덱스 매핑에 없음 (floorplans: revision). Searchable::Floorplan 매핑(app/models/concerns/searchable/floorplan.rb:33-109) 에는 revision 정의가 없고, as_indexed_json(:119-157) 직렬화 필드 목록에도 없다. 참고로 panos 매핑에서도 revision 은 명시적으로 주석 처리돼 있다(searchable/pano.rb:34 부근에 id 는 있으나 revision 라인 및 :139 # :revision = 색인 대상에서 제외). 모든 searchable 인덱스가 mappings dynamic: 'true'(searchable/floorplan.rb:33, pano.rb:33) 이지만, dynamic 매핑은 해당 필드를 담은 문서가 색인될 때만 생성되므로, 문서에 한 번도 실린 적 없는 필드(revision)로 정렬하면 여전히 "No mapping found" 가 난다.

live 재현 (Kibana ES --sort, env qa) — 위 판정을 실제 인덱스에 대해 검증:

text
floorplans sort=revision:desc → [400] query_shard_exception "No mapping found for [revision] in order to sort on" index=floorplans   (실패 재현)
floorplans sort=id:desc       → OK (정상 반환)
panos      sort=id:desc       → OK (정상 반환, id=376677 문서)
panos      sort=updated_at:desc → OK (정상 반환)

id·updated_atpanos/floorplans 양쪽에서 정렬 가능하며(빠지지 않음), 실제로 인덱스 매핑에서 "빠진" 정렬 필드는 floorplansrevision 뿐이다. 나머지 panos 실패(-id, id desc, updated_at desc)는 매핑 누락이 아니라 클라이언트가 만든 잘못된 필드명이다.

text
2026-07-27T06:10:49Z  [502] GET /api/v1/floorplans  reason="No mapping found for [revision] in order to sort on"  index=floorplans
2026-07-27T06:14:27Z  [502] GET /api/v1/floorplans  reason="No mapping found for [revision] in order to sort on"  index=floorplans
2026-07-27T08:56:01Z  [502] GET /api/v1/floorplans  reason="No mapping found for [revision] in order to sort on"  index=floorplans

service:cupixworks-api "ARG13000" 검색은 0건 — 이 클러스터의 응답은 ARG13000 이 아니라 BG10002(502) 임을 재확인.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 클라이언트가 인덱스 매핑에 없는 필드로 정렬 요청 → build_sort_query 가 검증 없이 ES 에 전달 → ES query_shard_exception, tesla 는 502 로 mis-map searchable_controller.rb:399-402 화이트리스트 밖 order_by.to_sym 그대로 전달; Datadog 로그 raw ES No mapping found for [-id]/[revision]/...; 응답코드 [502] + error.message 에 raw ES JSON Confirmed
H2 실제 ES 클러스터 장애/불가용(BG10002 의 일반적 원인) 응답코드가 BG10002/502 error.messagequery_shard_exception/No mapping found (샤드 매핑 오류)로 클러스터 다운이 아님; status-board svc:cupixworks-api::unknown active 없음 Rejected
H3 Representative [id] 가 stale 이고 현재는 다른 root cause ET 가 first_seen 샘플을 고정 retention 창 로그가 동일 No mapping found ... in order to sort on (필드명만 id/-id/revision 등 변함) → 같은 root cause, stale 아님 Rejected
H4 서버 로직/스키마 결함 (컬럼/필드 누락 버그) 메시지에 필드명 등장 정렬 필드는 클라이언트가 임의로 보낸 파라미터(?sort/?order_by)이지 서버가 하드코딩한 필드가 아님; 정상 필드 요청은 성공 Rejected

Fix Recommendation#

참고: 이 클러스터는 코드 결함(bug)이 아닌 노이즈(client 입력 오류의 status-code mis-mapping)로 판정. 아래는 알람 노이즈 저감을 위한 선택적 개선안이며, 응답코드 변경은 프런트엔드 계약 조율이 필요하다.

즉시 조치 (Critical)#

  • 코드 변경 불필요. Error Tracking 에서 해당 이슈 IGNORE 권장.

단기 개선 (1주 이내)#

  • app/controllers/concerns/searchable_controller.rb:399-402 build_sort_query 에 정렬 필드 화이트리스트/인덱스 매핑 검증 추가. 검증은 관측된 두 실패 부류를 모두 잡아야 함: (1) malformed 필드명-id/id desc 처럼 부호·방향이 섞인 토큰은 id·updated_at 자체는 정렬 가능하더라도 sort key 로 쓰이기 전에 정규화(부호 → 방향 변환, 공백 분리)하거나 거부; (2) 매핑에 없는 필드floorplansrevision 처럼 인덱스에 없는 필드는 거부. 화이트리스트 밖·매핑 없는 필드는 Cupix::Errors::Parameter (ARG10001, HTTP 400) 로 거부하여 5xx 노이즈 제거. 단, 정렬 가능 필드 목록이 인덱스별로 다르므로 프런트엔드가 사용하는 정렬 필드 계약을 먼저 확인 후 화이트리스트 확정 (breaking change 방지).
  • 대안: base_repository.rb:83 의 ES BadRequest → ARG13000 rescue 가 실제로 동작하도록 _search/lazy ES 실행 시점을 search 의 begin 블록 안으로 보장. 다만 ARG13000 도 server_error_controller.rb:7-8 에서 Cupix::Errors::Argument → system_500_error(500) 로 매핑되므로, 근본적으로는 정렬 입력 오류를 Cupix::Errors::Parameter(400) 로 좁혀야 한다.

장기 개선 (재발 방지)#

  • 컨트롤러 진입부에서 정렬/필터 파라미터 스키마 검증(허용 필드·방향 화이트리스트) 도입.
  • Cupix::Errors::Argument 를 통째로 400 으로 옮기지 말 것 — entity_repository.rb 등 광범위 사용. 정렬 무매핑 케이스만 Parameter 로 좁혀 raise.

Monitoring#

정렬 무매핑 502 발생 추이:

text
service:cupixworks-api "in order to sort on"

목록 API 502 응답 추이 (정렬 오류 표면화 여부 확인):

text
service:cupixworks-api @http.status_code:502 ("No mapping found" OR "query_shard_exception")

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard (프런트엔드 정렬 필드 계약 조율 필요 시 상승)

Noise Verdict#

noise — 클라이언트가 잘못된 정렬 파라미터(?order_by=-id/?order_by=id desc/?order_by=revision 등)를 보낸 입력 오류가 5xx(502 BG10002)로 잘못 매핑된 것으로, 서버 로직 결함이 아니라 코드 변경 없이 무시 가능한 노이즈다.

Revision History#

Revision 1#

Feedback: "어떤 index 에 어떤게 빠져있는지 전체적으로 확인좀" — 인덱스별로 어떤 정렬 필드가 매핑에서 빠져 있는지 전수 확인 요청.

판정:

피드백 항목 판정 근거
인덱스별 "빠진 정렬 필드" 전수 확인 수용 retention 창 8건 전수 로그 분석(attributes.params)로 인덱스·필드·컨트롤러를 확정: panos=-id/id desc/updated_at desc(각 1건), floorplans=revision(5건). 인덱스 매핑을 소스(app/models/concerns/searchable/pano.rb:34,97, floorplan.rb:33-109)와 Kibana --raw/--sort live 재현으로 대조.
(조사 결과 파생) 기존 RCA 의 "매핑에 없는 필드" 단일 프레이밍이 부정확 부분 수용 실패는 두 부류로 분리됨. id·updated_atpanos/floorplans 양쪽에 정렬 가능 필드로 매핑돼 있음(Kibana live: panos sort=id/updated_at, floorplans sort=id 모두 OK) → panos-id/id desc/updated_at desc 실패는 매핑 누락이 아니라 클라이언트가 부호·방향을 필드명에 섞은 malformed field name. 실제로 인덱스 매핑에서 "빠진" 필드는 floorplansrevision 뿐(searchable/floorplan.rb 에 정의 없음, Kibana live floorplans sort=revision:desc[400] No mapping found for [revision] 재현).

변경 사항:

  • ## Root Cause Summary: 실패 정렬 키를 (1) malformed field name (매핑은 존재) / (2) 매핑에 없는 필드 두 부류로 명시하도록 수정. 기존의 "존재하지 않는 필드(id, -id, ...)" 나열이 id/updated_at 를 오분류하던 것을 정정.
  • ### Log Evidence: "인덱스별 무엇이 빠졌는가 전수 분석" 하위 섹션 추가 — 인덱스×필드×매핑존재여부×실패원인 표(8건 전수) + Kibana ES --sort live 재현 4건.
  • ## Fix Recommendation 단기 개선: build_sort_query 검증이 두 실패 부류(malformed 필드명 정규화/거부 + 미매핑 필드 거부)를 모두 다루도록 보강.
  • ## Noise Verdict: 예시 파라미터를 실측 값(-id/id desc/revision)으로 교체.

추가 조사 내용:

  • tesla develop 브랜치 app/controllers/concerns/searchable_controller.rbSORTABLE_FIELDS_ALIAS(5개, :7-13), order_by/build_single_sort_query/build_multi_sort_query/build_sort_query(:362-402) 정렬 파이프라인 재확인. 단일 sort(order_by+sort)와 multi sort(sort_order_by, 공백 split) 문법 차이 확인.
  • 인덱스 매핑 소스: app/models/concerns/searchable/pano.rb(id integer/updated_at date 매핑 O, revision 색인 제외), searchable/floorplan.rb(id/updated_at 매핑 O, revision 정의 없음), 전 searchable 모듈 mappings dynamic: 'true'(단 api_client'false') 확인.
  • Datadog 로그(now-14d, service:cupixworks-api "in order to sort on" 8건)에서 attributes.params.order_by 원본 값과 컨트롤러/인덱스/UA/env 추출 — 전부 env:qa, 테스트 툴 UA(aiohttp, Apidog), 테스트 팀(hani/cqaautopass/qatest/admin). service:cupixworks-api "ARG13000" = 0 (응답은 BG10002/502 로 재확인).
  • Kibana(env qa) live 검증: panos --raw(문서에 id/updated_at 실재 확인), floorplans/panos --sortrevision(fail)·id·updated_at(ok) 정렬 재현.