{"record":{"id":"93792bef1b46b75f","repo":"getredash/redash","slug":"page-is-out-of-range","errorCode":null,"errorMessage":"Page is out of range.","messagePattern":"Page is out of range\\.","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"redash/handlers/base.py","lineNumber":87,"sourceCode":"\ndef get_object_or_404(fn, *args, **kwargs):\n    try:\n        rv = fn(*args, **kwargs)\n        if rv is None:\n            abort(404)\n    except NoResultFound:\n        abort(404)\n    return rv\n\n\ndef paginate(query_set, page, page_size, serializer, **kwargs):\n    count = query_set.count()\n\n    if page < 1:\n        abort(400, message=\"Page must be positive integer.\")\n\n    if (page - 1) * page_size + 1 > count > 0:\n        abort(400, message=\"Page is out of range.\")\n\n    if page_size > 250 or page_size < 1:\n        abort(400, message=\"Page size is out of range (1-250).\")\n\n    results = query_set.paginate(page, page_size)\n\n    # support for old function based serializers\n    if isclass(serializer):\n        items = serializer(results.items, **kwargs).serialize()\n    else:\n        items = [serializer(result) for result in results.items]\n\n    return {\"count\": count, \"page\": page, \"page_size\": page_size, \"results\": items}\n\n\ndef org_scoped_rule(rule):\n    if settings.MULTI_ORG:\n        return \"/<org_slug>{}\".format(rule)","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/handlers/base.py#L69-L105","documentation":"Raised by paginate() in redash/handlers/base.py when the requested page starts past the end of the result set, i.e. (page-1)*page_size + 1 exceeds the total count while count > 0. It prevents Flask-SQLAlchemy's paginate from returning an empty items list for a nonsensical page.","triggerScenarios":"Requesting a page beyond the last one, e.g. 30 results with page_size=25 and ?page=3 (starts at item 51 > 30). Typically after data is deleted or filters shrink the result set while a client keeps a stale page parameter.","commonSituations":"Deleting many rows then refreshing a paginated view that still points to the old page; concurrent filtering that reduces counts; hardcoding a page number that only existed with older data.","solutions":["Re-query page 1 or clamp page to ceil(count/page_size) when you get this 400.","Refresh total count before requesting later pages; re-read the 'count' field from the response of the previous page.","Pass a smaller page number or larger page_size within the 1-250 limit."],"exampleFix":"# before\npage = stale_page  # e.g. 3 but only 1 page exists\nresp = client.get(f'/api/queries?page={page}&page_size=25')\n\n# after\nresp = client.get(f'/api/queries?page={min(stale_page, max(1, count // 25 + 1))}&page_size=25')","handlingStrategy":"validation","validationCode":"count = resp_json['count']\nmax_page = max(1, -(-count // page_size))\npage = min(page, max_page)","typeGuard":null,"tryCatchPattern":"try:\n    resp = client.get(url)\nexcept HTTPError as e:\n    if e.response.status_code == 400 and 'out of range' in e.response.text:\n        resp = client.get(url_with_page_1)\n    else:\n        raise","preventionTips":["Always read 'count' from the API response and compute the last page locally.","After deletions or filter changes, restart pagination from page 1."],"tags":["pagination","bad-request","redash","api"],"backgroundTag":"pagination-page-out-of-range","analyzedSha":"ca79fe988d81cdac9675b412f3dfcab107bc1fbc","analyzedAt":"2026-08-28T18:32:34.637Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}