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#
- 2025-01-23 14:01 KST — 최초 발생 (first_seen)
- 2026-07-20 16:01 KST — 클러스터 last_seen (Error Tracking 집계 기준)
- 2026-07-27 09:18 KST — retention 창 내 실제 로그 확인 (
[502] GET /api/v1/panos, sort field[-id]/[id desc]/[id]) - 2026-07-27 15:10~17:56 KST —
[502] GET /api/v1/floorplans, sort field[revision]반복 - 2026-08-04 — RCA 수행
Error Log#
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::BadRequest 를 server_error_controller.rb:12-13 의 rescue_from Elasticsearch::Transport::Transport::Error → badgateway_on_elasticsearch_502_error 로 잡아 HTTP 502 BG10002 로 응답한다.
관측된 실패 정렬 키는 두 부류다(retention 창 8건 전수 분석, 상세는 아래 Log Evidence 표): (1) 매핑은 존재하지만 필드명이 malformed — panos 의 -id, id desc, updated_at desc (id·updated_at 은 panos/floorplans 양쪽에 정렬 가능 필드로 매핑돼 있으나, 클라이언트가 방향/부호를 sort 파라미터로 분리하지 않고 order_by 값 안에 섞어 넣어 "-id"/"id desc" 라는 존재하지 않는 필드명이 됨); (2) 필드 자체가 인덱스 매핑에 없음 — floorplans 의 revision (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_option→order_by→build_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— ESBadRequest→ 502 BG10002
컨트롤러 진입:
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 키로 사용 (매핑 검증 없음):
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 로 매핑:
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 로 도달했음을 확인:
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 쿼리 (재현):
service:cupixworks-api "in order to sort on"
service:cupixworks-api "No mapping found"
retention 창(now-14d, 기준 2026-08-04) 에서 8건 확인. 모두 status:info 요청 로그이며 응답코드 [502], error.message 필드에 raw ES 예외가 담김:
{
"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 |
즉, 정렬 실패는 두 부류로 나뉜다:
- 매핑은 존재하나 필드명이 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) 이다. - 필드 자체가 인덱스 매핑에 없음 (
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) — 위 판정을 실제 인덱스에 대해 검증:
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_at 은 panos/floorplans 양쪽에서 정렬 가능하며(빠지지 않음), 실제로 인덱스 매핑에서 "빠진" 정렬 필드는 floorplans 의 revision 뿐이다. 나머지 panos 실패(-id, id desc, updated_at desc)는 매핑 누락이 아니라 클라이언트가 만든 잘못된 필드명이다.
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.message 가 query_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-402build_sort_query에 정렬 필드 화이트리스트/인덱스 매핑 검증 추가. 검증은 관측된 두 실패 부류를 모두 잡아야 함: (1) malformed 필드명 —-id/id desc처럼 부호·방향이 섞인 토큰은id·updated_at자체는 정렬 가능하더라도 sort key 로 쓰이기 전에 정규화(부호 → 방향 변환, 공백 분리)하거나 거부; (2) 매핑에 없는 필드 —floorplans의revision처럼 인덱스에 없는 필드는 거부. 화이트리스트 밖·매핑 없는 필드는Cupix::Errors::Parameter(ARG10001, HTTP 400) 로 거부하여 5xx 노이즈 제거. 단, 정렬 가능 필드 목록이 인덱스별로 다르므로 프런트엔드가 사용하는 정렬 필드 계약을 먼저 확인 후 화이트리스트 확정 (breaking change 방지).- 대안:
base_repository.rb:83의 ESBadRequest → ARG13000rescue 가 실제로 동작하도록_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 발생 추이:
service:cupixworks-api "in order to sort on"
목록 API 502 응답 추이 (정렬 오류 표면화 여부 확인):
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_at 은 panos/floorplans 양쪽에 정렬 가능 필드로 매핑돼 있음(Kibana live: panos sort=id/updated_at, floorplans sort=id 모두 OK) → panos 의 -id/id desc/updated_at desc 실패는 매핑 누락이 아니라 클라이언트가 부호·방향을 필드명에 섞은 malformed field name. 실제로 인덱스 매핑에서 "빠진" 필드는 floorplans 의 revision 뿐(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--sortlive 재현 4건.## Fix Recommendation단기 개선:build_sort_query검증이 두 실패 부류(malformed 필드명 정규화/거부 + 미매핑 필드 거부)를 모두 다루도록 보강.## Noise Verdict: 예시 파라미터를 실측 값(-id/id desc/revision)으로 교체.
추가 조사 내용:
- tesla
develop브랜치app/controllers/concerns/searchable_controller.rb—SORTABLE_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(idinteger/updated_atdate 매핑 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 --sort로revision(fail)·id·updated_at(ok) 정렬 재현.