getredash/redash · error · QueryParseError

'pagination.fields' should be a list of 2 field names

Error message

'pagination.fields' should be a list of 2 field names

What it means

TokenPagination requires 'pagination.fields' to be a list of exactly two strings: the field in the response holding the next-page token (default 'next_page_token') and the request parameter to send it in (default 'page_token'). Any other shape is rejected at construction.

Source

Thrown at redash/query_runner/json_ds.py:266

    def __init__(self, pagination):
        self.path = pagination.get("path", "_links.next.href")
        if not isinstance(self.path, str):
            raise QueryParseError("'pagination.path' should be a string")

    def next(self, url, request_options, response):
        next_url = _apply_path_search(response, self.path, "")
        if not next_url:
            return False, None, request_options

        next_url = urljoin(url, next_url)
        return True, next_url, request_options


class TokenPagination(RequestPagination):
    def __init__(self, pagination):
        self.fields = pagination.get("fields", ["next_page_token", "page_token"])
        if not isinstance(self.fields, list) or len(self.fields) != 2:
            raise QueryParseError("'pagination.fields' should be a list of 2 field names")

    def next(self, url, request_options, response):
        next_token = _apply_path_search(response, self.fields[0], "")
        if not next_token:
            return False, None, request_options

        params = request_options.get("params", {})

        # prevent infinite loop that can happen if self.fields[1] is wrong
        if next_token == params.get(self.fields[1]):
            raise Exception("{} did not change; possible misconfiguration".format(self.fields[0]))

        params[self.fields[1]] = next_token
        request_options["params"] = params
        return True, url, request_options


register(JSON)

View on GitHub (pinned to ca79fe988d)

Solutions

  1. Use a 2-element list: [response_field, request_param], e.g. ["next_cursor", "cursor"]
  2. Omit fields to use the defaults ["next_page_token", "page_token"]
  3. Confirm both names against the API's response body and accepted query parameters

Example fix

// before
{"pagination": {"type": "token", "fields": "next_cursor"}}
// after
{"pagination": {"type": "token", "fields": ["next_cursor", "cursor"]}}
Defensive patterns

Strategy: validation

Validate before calling

fields = cfg.get('pagination', {}).get('fields', ['next_page_token', 'page_token'])
assert isinstance(fields, list) and len(fields) == 2 and all(isinstance(f, str) for f in fields)

Type guard

def valid_fields(p: dict) -> bool:
    f = p.get('fields', ['next_page_token', 'page_token'])
    return isinstance(f, list) and len(f) == 2 and all(isinstance(x, str) for x in f)

Prevention

When it happens

Trigger: Setting pagination.fields to a string, a list of 1 or 3 items, or a dict, e.g. {"pagination": {"type": "token", "fields": "next_cursor"}}.

Common situations: Developer only specifies the response field and forgets the request param name, or copies field names from the API docs as a single string instead of a two-element list.

Related errors


AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28). Data as JSON: /api/errors/b8737d14c7ccea8a. Report an issue: GitHub.