{"record":{"id":"d154c0f0b98abfbe","repo":"PrefectHQ/fastmcp","slug":"invalid-params-d154c0","errorCode":"INVALID_PARAMS","errorMessage":"str(e) (from paginate_sequence ValueError, wrapped as INVALID_PARAMS)","messagePattern":"str\\(e\\) \\(from paginate_sequence ValueError, wrapped as INVALID_PARAMS\\)","errorType":"error_code","errorClass":"MCPError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/mixins/mcp_operations.py","lineNumber":66,"sourceCode":"\nPaginateT = TypeVar(\"PaginateT\")\n\n\ndef _apply_pagination(\n    items: Sequence[PaginateT],\n    cursor: str | None,\n    page_size: int | None,\n) -> tuple[list[PaginateT], str | None]:\n    \"\"\"Apply pagination to items, raising MCPError for invalid cursors.\n\n    If page_size is None, returns all items without pagination.\n    \"\"\"\n    if page_size is None:\n        return list(items), None\n    try:\n        return paginate_sequence(items, cursor, page_size)\n    except ValueError as e:\n        raise MCPError(code=INVALID_PARAMS, message=str(e)) from e\n\n\ndef _normalize_call_tool_result(\n    result: Any,\n) -> mcp_types.CallToolResult:\n    \"\"\"Normalize a tool's ``to_mcp_result()`` output into a ``CallToolResult``.\n\n    ``ToolResult.to_mcp_result()`` returns one of three shapes for backward\n    compatibility: a ``CallToolResult`` (error/meta case), a bare\n    ``list[ContentBlock]`` (unstructured), or a ``(content, structured)`` tuple.\n    The SDK v2 runner requires a ``BaseModel`` result, so wrap the shorthand\n    forms here (the SDK's old ``call_tool`` decorator used to do this).\n    \"\"\"\n    if isinstance(result, mcp_types.CallToolResult):\n        return result\n    if isinstance(result, tuple):\n        content, structured = result\n        return mcp_types.CallToolResult(content=content, structured_content=structured)","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/mixins/mcp_operations.py#L48-L84","documentation":"Raised as MCPError with JSON-RPC code INVALID_PARAMS by _apply_pagination when paginate_sequence() raises ValueError while slicing the items list. This means the client-supplied pagination cursor was malformed (not a valid base64/encoded offset for the sequence), so the list_* operation cannot resolve the page. The original ValueError text is preserved as the message.","triggerScenarios":"Calling list_tools / list_resources / list_resource_templates / list_prompts with a cursor argument that is not a valid cursor previously returned by the server — e.g. a hand-crafted string, a cursor from a different list with different page_size, or a truncated/garbled cursor.","commonSituations":"Manually constructing cursors instead of echoing the server's nextCursor; persisting cursors across server restarts or page_size changes; page_size differing between the call that issued the cursor and the call reusing it.","solutions":["Reuse the nextCursor value returned by the previous list_* response verbatim — never fabricate or edit cursors.","Keep page_size constant across the pages of one listing; changing it invalidates cursors.","If a cursor is stale or unknown, restart pagination from the first page (cursor=None) with the same page_size."],"exampleFix":"// before: hand-made cursor\npage = await client.list_tools(cursor=\"2\")\n\n// after: echo the server's cursor\nfirst = await client.list_tools(page_size=10)\nif first.next_cursor:\n    page = await client.list_tools(cursor=first.next_cursor, page_size=10)","handlingStrategy":"fallback","validationCode":"def is_valid_cursor(cursor: str | None) -> bool:\n    if cursor is None:\n        return True\n    import base64\n    try:\n        base64.b64decode(cursor, validate=True)\n        return True\n    except Exception:\n        return False\n\nif not is_valid_cursor(cursor):\n    cursor = None  # restart pagination from the first page","typeGuard":null,"tryCatchPattern":"try:\n    page = await client.list_tools(cursor=cursor, page_size=page_size)\nexcept Exception as e:\n    if \"INVALID_PARAMS\" in str(e) or \"cursor\" in str(e).lower():\n        cursor = None  # discard stale/invalid cursor, restart from page 1\n        page = await client.list_tools(page_size=page_size)\n    else:\n        raise","preventionTips":["Only use nextCursor values returned by the server, unmodified.","Keep page_size fixed while iterating one listing.","Don't persist cursors across server restarts or deployments.","On any pagination error, fall back to cursor=None and re-list instead of retrying the bad cursor."],"tags":["pagination","invalid-params","mcp"],"backgroundTag":"invalid-pagination-cursor","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}