{"record":{"id":"25f72faa67a2c1a9","repo":"Comfy-Org/ComfyUI","slug":"invalid-cursor-25f72f","errorCode":"INVALID_CURSOR","errorMessage":"cursor exceeds maximum length","messagePattern":"cursor exceeds maximum length","errorType":"http","errorClass":"InvalidCursorError","httpStatus":400,"severity":"error","filePath":"app/assets/services/cursor.py","lineNumber":127,"sourceCode":"    cursor: str,\n    allowed_sort_fields: Iterable[str],\n    expected_order: str | None = None,\n) -> CursorPayload:\n    \"\"\"Parse an opaque cursor.\n\n    ``allowed_sort_fields`` is the endpoint's accepted sort-field list — a\n    cursor carrying a field outside this set is rejected so a cursor minted\n    for one column can't be replayed against another (e.g. a ``created_at``\n    timestamp string compared against a ``name`` column).\n\n    ``expected_order`` (``\"asc\"``/``\"desc\"``), when supplied, must match the\n    payload's ``o`` field. ``o`` is required on every payload; a cursor\n    missing it is rejected as malformed.\n\n    Passing no allowed fields rejects every cursor.\n    \"\"\"\n    if len(cursor) > MAX_ENCODED_CURSOR_LENGTH:\n        raise InvalidCursorError(\"cursor exceeds maximum length\")\n\n    try:\n        # urlsafe_b64decode requires correct padding; we strip on encode, so\n        # restore the trailing '=' pad here.\n        padding = \"=\" * (-len(cursor) % 4)\n        raw = base64.urlsafe_b64decode(cursor + padding)\n    except (ValueError, base64.binascii.Error) as e:\n        raise InvalidCursorError(f\"encoding: {e}\") from e\n\n    try:\n        decoded = json.loads(raw)\n    except (json.JSONDecodeError, UnicodeDecodeError) as e:\n        raise InvalidCursorError(f\"payload: {e}\") from e\n\n    if not isinstance(decoded, dict):\n        raise InvalidCursorError(\"payload: expected object\")\n\n    sort_field = decoded.get(\"s\")","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/app/assets/services/cursor.py#L109-L145","documentation":"InvalidCursorError raised at the top of decode_cursor when the encoded cursor string exceeds MAX_ENCODED_CURSOR_LENGTH (8192 characters). The length cap is a cheap first line of defense before any base64/JSON parsing, bounding payload size before decode work is done.","triggerScenarios":"Sending an after parameter longer than 8192 chars — typically not a real cursor but accidentally attached data: a full URL, a JSON blob, a pasted token, or a proxy/gateway appending junk to query parameters.","commonSituations":"Query-string corruption by middlewares or hand-built URLs; clients copying an entire response body into the after field; a copied URL whose cursor param got duplicated repeatedly by a buggy link builder.","solutions":["Check len(cursor) <= 8192 client-side before sending and treat oversized values as session-reset.","Verify you are passing exactly the next_cursor string from the prior response, unmodified and URL-encoded once.","Reset pagination and fetch page one when an oversized/invalid cursor is detected.","Return 400 INVALID_CURSOR rather than retrying the same token."],"exampleFix":"# before\nresp = list_assets(after=params.get(\"after\", \"\"))\n\n# after\nraw = params.get(\"after\", \"\")\nafter = raw if raw and len(raw) <= 8192 else None\nresp = list_assets(after=after)","handlingStrategy":"validation","validationCode":"MAX_LEN = 8192\n\ndef usable_cursor(raw: str | None) -> str | None:\n    return raw if raw and len(raw) <= MAX_LEN else None","typeGuard":null,"tryCatchPattern":"try:\n    page = list_assets(after=after)\nexcept InvalidCursorError:\n    page = list_assets()  # reset to page one","preventionTips":["Send next_cursor verbatim and URL-encode it exactly once.","Length-check tokens client-side and reset pagination when they look wrong.","Never paste unrelated data into the after parameter."],"tags":["pagination","cursor","validation","bad-request"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}