Cupix::Errors::Argument: Result window is too large, from + size must be less than or equal to: [10000] but was [10200].
RCA: Cupix::Errors::Argument: Result window is too large
Overview#
What Happened#
cupixworks-api(tesla)의 GET /api/v1/admin/editings 목록 조회에서, 클라이언트가 Elasticsearch 의 index.max_result_window(기본 10,000) 를 넘어서는 깊은 페이지를 요청했다. ES 가 from + size = 10200 > 10000 이라며 BadRequest 를 던지고, tesla 는 이를 Cupix::Errors::Argument(ARG13000) 로 감싼 뒤 HTTP 500 으로 응답했다. 클라이언트 페이지네이션 입력 문제가 서버 500 으로 잘못 매핑된 케이스다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Cupix::Errors::Argument |
| exception.message | Result window is too large, from + size must be less than or equal to: [10000] but was [10200]. |
| error.code | ARG13000 |
| top_frame | app/repositories/base_repository.rb:87 |
| entry point | app/controllers/api/v1/admin/editings_controller.rb:12 (Api::V1::Admin::EditingsController#index) |
| http.status | 500 |
| env | production, cupixworks-api |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| admin (editing 목록 조회 UI) | 14일 내 9건 | 깊은 페이지 요청 시 목록 조회 500. 첫 페이지 이후 계속 페이징하는 관리자에게만 발생 |
Timeline#
- 2025-02-03 13:06 KST — 최초 발생 (
first_seen). - 2026-08-02 09:18-09:20 KST — 동일 엔드포인트에서 4건 연속 발생 (같은 사용자가 반복 페이징한 것으로 추정되는 버스트).
- 2026-08-02 19:11 KST — 가장 최근 발생 (
last_seen).
Error Log#
Result window is too large, from + size must be less than or equal to: [10000] but was [10200]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.
Impact#
- Service:
cupixworks-api - 발생 횟수: 130 (전체 기간), 14일 내 9건
- 최초 발생: 2025-02-03 13:06 KST
- 최근 발생: 2026-08-02 19:11 KST
Root Cause Summary#
Api::V1::Admin::EditingsController#index 는 클라이언트의 page 와 per_page 파라미터로 QueryOption 을 만들어 Elasticsearch 검색을 수행한다. Cupix::QueryOption::Base 는 page >= 1, per_page <= 300 만 검증할 뿐 from + size = (page - 1) * per_page + per_page 값이 ES 의 index.max_result_window(기본 10,000) 를 넘는지는 검사하지 않는다. 클라이언트가 깊은 페이지(예: page=34, per_page=300 → 10,200)를 요청하면 ES 가 illegal_argument_exception 을 담은 BadRequest 를 반환하고, base_repository.rb:83-87 이 이를 Cupix::Errors::Argument(ARG13000) 로 감싼다. 이 예외는 server_error_controller.rb:7-8 의 system_500_error 로 rescue 되어 HTTP 500 이 된다. 즉 근본 원인은 서버 로직 결함이 아니라 클라이언트 페이지네이션 한계 입력이며, 여기에 클라이언트 오류(4xx)가 서버 오류(500)로 매핑되는 상태 코드 결함이 겹친 것이다.
Technical Analysis#
Code Path#
- Entry point:
app/controllers/api/v1/admin/editings_controller.rb:12
def index
editing_query_option = Cupix::QueryOption::Editing.new(get_query_option(enable_current_team: false), params)
editings = repository_instance.search(editing_query_option)
get_query_option이 클라이언트page/per_page를 그대로QueryOption에 전달한다.
def get_query_option(enable_current_team: true)
_query_option = QueryOption.new(
per_page: params[:per_page],
page: params[:page],
...
)
QueryOption::Base는page하한(>= 1)과per_page상한(<= 300)만 검증한다.page상한이나from + size검증은 없다.
raise Cupix::Errors::Parameter.new(code: 'ARG10001', reason: "Invalid value in page: #{opts[:page]}") if !opts[:page].nil? && opts[:page].to_i < 1
raise Cupix::Errors::Parameter.new(code: 'ARG10001', reason: "Invalid value in per_page: #{opts[:per_page]}") if !opts[:per_page].nil? && opts[:per_page].to_i < 1
# ...
@page = opts[:page].nil? ? default_page : opts[:page].to_i
@per_page = opts[:per_page].nil? ? default_per_page : opts[:per_page].to_i
# ...
if @per_page.present? && @per_page > 300
raise Cupix::Errors::Parameter.new(code: 'ARG10001', reason: "Invalid per_page: #{@per_page}; maximum is 300")
end
- Failure point:
app/repositories/base_repository.rb:83-87.search실행 중permission_joins/default_joins가 ES 쿼리를 lazy 하게 materialize 할 때 ES 가BadRequest를 던지고,root_cause[0]['reason']("Result window is too large...")을Cupix::Errors::Argument(ARG13000)로 감싼다.
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)
Cupix::Errors::Argument는Cupix::Errors::System과 함께system_500_error로 rescue 되어 500 이 된다. 클라이언트 페이지네이션 오류인데 서버 500 으로 노출되는 지점이다.
rescue_from Cupix::Errors::System,
Cupix::Errors::Argument, with: :system_500_error
# ...
def system_500_error(exception)
raise_error(500, exception)
end
기대 동작: 페이지네이션 한계를 넘는 요청은 4xx(예: 400/422)로 거절되거나, 애초에 from + size <= 10000 범위로 제한되어야 한다. 실제 동작: 10200 요청이 ES 를 통과 시도 → BadRequest → ARG13000 → HTTP 500 + Error Tracking 집계.
Log Evidence#
Datadog 쿼리 (재현용):
service:cupixworks-api "Result window is too large"
service:cupixworks-api "[500] GET /api/v1/admin/editings"
최근 발생 로그 (원문, status:info 로 기록된 500 request 로그):
{
"timestamp": "2026-08-02 10:11:03",
"status": "info",
"message": "[500] GET /api/v1/admin/editings (Api::V1::Admin::EditingsController#index)",
"error": {
"reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10200]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.",
"code": "ARG13000",
"message": "Result window is too large, from + size must be less than or equal to: [10000] but was [10200]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.",
"class": "Cupix::Errors::Argument"
}
}
14일 내 service:cupixworks-api "[500] GET /api/v1/admin/editings" 발생 타임스탬프 (UTC):
2026-08-02 10:11:03
2026-08-02 00:20:13
2026-08-02 00:19:45
2026-08-02 00:19:11
2026-08-02 00:18:41
2026-08-01 17:26:41
2026-08-01 17:23:55
2026-08-01 16:30:27
2026-07-25 11:44:31
패턴: 2026-08-02 00:18-00:20 UTC 사이 4건이 30초 간격으로 연속 발생 — 동일 사용자가 목록을 계속 페이징하며 매번 500 을 받은 버스트로 해석된다. 모든 발생이 동일 엔드포인트(admin/editings)와 동일 10200 값을 가진다. Representative Error 와 최근 로그가 정확히 일치하므로 대표 샘플은 stale 하지 않다.
주의: 같은 "Result window is too large" 검색은 다른 엔드포인트(GET /api/v1/element_traces, max_result_window: 200000, 292800, [502]/[400])의 로그도 섞여 나온다. 그 변형은 다른 코드 경로(entity/element_traces 검색, 상한 200,000)이며 본 클러스터(admin/editings, 상한 10,000, ARG13000, 500)와 근본 원인은 같은 계열이나 별개 이슈다. 본 RCA 는 admin/editings 500 경로에 한정한다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 클라이언트가 ES max_result_window(10,000)를 넘는 깊은 페이지를 요청 → ES BadRequest → ARG13000 → 500. page/from+size 상한 미검증 + 4xx→500 매핑 결함 |
Datadog: [500] GET /api/v1/admin/editings 로그의 error.code:ARG13000, from + size ... [10000] but was [10200]. 코드: base.rb:55-83 는 page 상한/from+size 미검증, base_repository.rb:87 가 ARG13000 로 wrap, server_error_controller.rb:7-8 가 500 매핑 |
없음 | Confirmed |
| H2 | 서버측 검색 로직/인덱스 결함으로 항상 실패 | — | 10200 은 page/per_page 조합에서만 나오는 클라이언트 결정값. 첫 페이지 요청은 정상(에러가 깊은 페이지 버스트에 국한). 특정 데이터/인덱스가 아니라 페이지 깊이에만 의존 |
Rejected |
| H3 | ES 클러스터 장애/max_result_window 설정 변경 |
— | status-board svc:cupixworks-api::unknown 에 관련 active 인시던트 없음. 10000 은 ES 기본값이며 변경 흔적 없음. 메시지가 결정적(deterministic) |
Rejected |
| H4 | 대표 샘플이 stale 하고 실제 최근 메시지는 다른 값 | — | 최근 로그(2026-08-02)와 대표 메시지가 문자 그대로 일치(10200) |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 상태 코드 매핑 교정:
admin/editings(및 동일 검색 경로)에서 ESmax_result_window초과는 클라이언트 요청 오류이므로 4xx 로 응답해야 한다.base_repository.rb:83-87의Cupix::Errors::Argument(ARG13000) 는server_error_controller.rb:7-8에서 500 으로 매핑되므로, 이 페이지네이션 초과 케이스만 4xx(예:Cupix::Errors::Parameter계열)로 분류하도록 좁혀야 한다.Cupix::Errors::Argument를 전역으로 4xx 로 옮기면 다른 사용처에 영향을 주므로 금지 (ARG10060/to_i에피소드와 동일한 주의).
단기 개선 (1주 이내)#
- 페이지네이션 상한 사전 검증:
lib/cupix/query_option/base.rb에서per_page > 300검증과 함께(page - 1) * per_page + per_page > ES_DEFAULT_MAX_RESULT_WINDOW인 경우를 사전에Cupix::Errors::Parameter(4xx) 로 거절하도록 추가한다. 이렇게 하면 ES 로 요청이 나가기 전에 명확한 클라이언트 오류로 응답하고 Error Tracking 노이즈도 제거된다.ES_DEFAULT_MAX_RESULT_WINDOW상수(app/models/concerns/searchable/*에서 사용)를 참조. - 프런트엔드 협의 필요: admin editing 목록 UI 가 10,000 건 이상 페이징을 시도하지 않도록 페이저 상한을 두거나, 깊은 탐색이 필요하면 필터/검색으로 유도한다. 이 항목은 클라이언트 계약 변경이므로 백엔드 자동 수정 범위 밖.
장기 개선 (재발 방지)#
- 대량 조회가 실제로 필요하면
from/size대신 ESsearch_after(scroll 대체) 기반 커서 페이지네이션 도입을 검토한다. 오류 메시지도 "See the scroll api" 를 권한다.
Monitoring#
admin/editings 페이지네이션 초과 500 추이:
service:cupixworks-api "[500] GET /api/v1/admin/editings" "Result window is too large"
ES result-window 초과 계열 전반(엔드포인트 무관) 추이:
service:cupixworks-api "Result window is too large"
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard (상태 코드 매핑 교정 + query_option 사전 검증은 좁은 범위 변경이나, 프런트엔드 페이저 상한은 별도 트랙 조율 필요)
Noise Verdict#
noise — ES max_result_window(10,000)를 초과하는 클라이언트의 깊은 페이지 요청이 원인이며 서버 로직 결함이 아니라 클라이언트 입력 검증/상태 코드 매핑 문제이므로 noise 로 판정한다.