{"record":{"id":"e672fc10a2a6682c","repo":"gitlabhq/gitlabhq","slug":"updated-at-filter-requires-updated-at-sort","errorCode":null,"errorMessage":"`updated_at` filter requires `updated_at` sort","messagePattern":"`updated_at` filter requires `updated_at` sort","errorType":"exception","errorClass":"DeploymentsFinder::InefficientQueryError","httpStatus":400,"severity":"error","filePath":"app/finders/deployments_finder.rb","lineNumber":61,"sourceCode":"  def execute\n    items = init_collection\n    items = by_updated_at(items)\n    items = by_finished_at(items)\n    items = by_environment(items)\n    items = by_status(items)\n    items = preload_associations(items)\n    sort(items)\n  end\n\n  private\n\n  def validate!\n    if filter_by_updated_at? && filter_by_finished_at?\n      raise InefficientQueryError, 'Both `updated_at` filter and `finished_at` filter can not be specified'\n    end\n\n    if filter_by_updated_at? && !order_by_updated_at?\n      raise InefficientQueryError, '`updated_at` filter requires `updated_at` sort'\n    end\n\n    if filter_by_finished_at? && !order_by_finished_at?\n      raise InefficientQueryError, '`finished_at` filter requires `finished_at` sort.'\n    end\n\n    if order_by_finished_at? && !(filter_by_finished_at? || filter_by_finished_statuses?)\n      raise InefficientQueryError,\n        '`finished_at` sort requires `finished_at` filter or a filter with at least one of the finished statuses.'\n    end\n\n    if filter_by_finished_at? && !filter_by_successful_deployment?\n      raise InefficientQueryError, '`finished_at` filter must be combined with `success` status filter.'\n    end\n\n    if filter_by_environment_name? && !params[:project].present?\n      raise InefficientQueryError, '`environment` name filter must be combined with `project` scope.'\n    end","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/gitlabhq/gitlabhq/blob/55ee20384a1f55cb0e362dee1d07149613b1b9bf/app/finders/deployments_finder.rb#L43-L79","documentation":"DeploymentsFinder#validate! raises InefficientQueryError when the updated_at filter is present but the sort is not order_by=updated_at. The filter only stays efficient when results are scanned in updated_at order via its supporting index, so GitLab requires sort and filter to match and returns HTTP 400 otherwise.","triggerScenarios":"GET /api/v4/projects/:id/deployments?updated_after=... sent without order_by, or with order_by=id / order_by=finished_at; SDK defaults that force order_by=id; pagination params copied from another endpoint.","commonSituations":"Clients adding time filters to an existing sorted listing; GraphQL/Automation templates that preset a default sort; keyset pagination code that sets its own order_by.","solutions":["Add order_by=updated_at (with sort=asc or sort=desc) whenever you send updated_after/updated_before.","If you must keep a different sort, drop the updated_at filter entirely.","Use the updated_at ordering together with keyset pagination for large result sets."],"exampleFix":"# before\nGET /api/v4/projects/42/deployments?updated_after=2026-01-01T00:00:00Z\n# => 400 `updated_at` filter requires `updated_at` sort\n\n# after\nGET /api/v4/projects/42/deployments?updated_after=2026-01-01T00:00:00Z&order_by=updated_at&sort=desc","handlingStrategy":"validation","validationCode":"if params[:updated_after] || params[:updated_before]\n  params[:order_by] ||= 'updated_at'\n  raise ArgumentError, 'updated_at filter needs order_by=updated_at' unless params[:order_by] == 'updated_at'\nend","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Bind time filters and sort in one request-builder method so they cannot diverge.","Disallow user-supplied order_by when a time filter is active.","Cover filter+sort combinations in client contract tests."],"tags":["gitlab","deployments","filter-validation","api-params","sorting"],"backgroundTag":"filter-sort-mismatch","analyzedSha":"55ee20384a1f55cb0e362dee1d07149613b1b9bf","analyzedAt":"2026-08-21T14:22:27.782Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}