Comfy-Org/ComfyUI · error · InvalidCursorError

INVALID_CURSOR

INVALID_CURSOR

Error message

cursor pagination is not supported for sort={sort!r}

What it means

InvalidCursorError raised by the listing service when a request supplies an `after` cursor while `sort` is not one of the cursor-supported fields (created_at, updated_at, name, size). The service only enters cursor mode for those sorts; other sort orders use offset pagination and cannot interpret a cursor.

Source

Thrown at app/assets/services/asset_management.py:301

    any_tags: Sequence[str] | None = None,
) -> ListAssetsResult:
    """List assets with optional cursor pagination.

    When ``after`` is supplied it overrides ``offset``. The cursor's sort field
    must match ``sort`` and be in the cursor-supported allowlist; mismatches
    raise InvalidCursorError so the handler can map to 400 INVALID_CURSOR.
    """
    cursor_value: object | None = None
    cursor_id: str | None = None
    # Mint next_cursor on every page where the sort is cursor-supported, not
    # only when the request itself arrived with a cursor. Otherwise a first
    # request (no `after`) returns next_cursor=None and the client can never
    # enter cursor mode.
    mint_cursor = sort in _CURSOR_SORT_FIELDS

    if after is not None:
        if sort not in _CURSOR_SORT_FIELDS:
            raise InvalidCursorError(
                f"cursor pagination is not supported for sort={sort!r}"
            )
        payload = decode_cursor(after, _CURSOR_SORT_FIELDS, expected_order=order)
        if payload.sort_field != sort:
            raise InvalidCursorError(
                f"cursor sort field {payload.sort_field!r} does not match request sort {sort!r}"
            )
        cursor_value, cursor_id = _resolve_cursor_value(payload), payload.id

    # Over-fetch by one row so we can distinguish "exactly `limit` rows total
    # remaining" from "more rows past this page" without a second query. Drop
    # the sentinel before returning.
    fetch_limit = limit + 1 if mint_cursor else limit

    with create_session() as session:
        refs, tag_map, total = list_references_page(
            session,
            owner_id=owner_id,

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Drop the after parameter when requesting a non-cursor-supported sort.
  2. Keep the sort stable across pages: reuse the exact sort that minted the cursor.
  3. If a new sort must support cursors, add the field to _CURSOR_SORT_FIELDS and implement its keyset comparison.
  4. Catch InvalidCursorError and return 400 INVALID_CURSOR so the client can reset to page one.

Example fix

# before
list_assets(sort="random", after=next_cursor)

# after
CURSOR_SORTS = {"created_at", "updated_at", "name", "size"}
list_assets(sort="random", after=next_cursor if sort in CURSOR_SORTS else None)
Defensive patterns

Strategy: validation

Validate before calling

CURSOR_SORTS = {"created_at", "updated_at", "name", "size"}

def safe_after(sort: str, after: str | None) -> str | None:
    return after if (after and sort in CURSOR_SORTS) else None

Try / catch

try:
    page = list_assets(sort=sort, after=after)
except InvalidCursorError as e:
    # 400: reset pagination
    page = list_assets(sort=sort, after=None)

Prevention

When it happens

Trigger: Calling the list endpoint with both after=<cursor> and a sort value outside ('created_at','updated_at','name','size'), e.g. sort=random or a custom sort, with a next_cursor carried over from a previous page.

Common situations: Client changes the sort control mid-pagination but keeps the after parameter; a new sort option added server-side without extending _CURSOR_SORT_FIELDS; saved/bookmarked URLs that embed an old cursor being replayed with a different sort.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/fd2e19eae5286c51. Report an issue: GitHub.