PrefectHQ/fastmcp · error · MCPError
INVALID_PARAMS
INVALID_PARAMS
Error message
str(e) (from paginate_sequence ValueError, wrapped as INVALID_PARAMS)
What it means
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.
Source
Thrown at fastmcp_slim/fastmcp/server/mixins/mcp_operations.py:66
PaginateT = TypeVar("PaginateT")
def _apply_pagination(
items: Sequence[PaginateT],
cursor: str | None,
page_size: int | None,
) -> tuple[list[PaginateT], str | None]:
"""Apply pagination to items, raising MCPError for invalid cursors.
If page_size is None, returns all items without pagination.
"""
if page_size is None:
return list(items), None
try:
return paginate_sequence(items, cursor, page_size)
except ValueError as e:
raise MCPError(code=INVALID_PARAMS, message=str(e)) from e
def _normalize_call_tool_result(
result: Any,
) -> mcp_types.CallToolResult:
"""Normalize a tool's ``to_mcp_result()`` output into a ``CallToolResult``.
``ToolResult.to_mcp_result()`` returns one of three shapes for backward
compatibility: a ``CallToolResult`` (error/meta case), a bare
``list[ContentBlock]`` (unstructured), or a ``(content, structured)`` tuple.
The SDK v2 runner requires a ``BaseModel`` result, so wrap the shorthand
forms here (the SDK's old ``call_tool`` decorator used to do this).
"""
if isinstance(result, mcp_types.CallToolResult):
return result
if isinstance(result, tuple):
content, structured = result
return mcp_types.CallToolResult(content=content, structured_content=structured)View on GitHub (pinned to 1f02114297)
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.
Example fix
// before: hand-made cursor
page = await client.list_tools(cursor="2")
// after: echo the server's cursor
first = await client.list_tools(page_size=10)
if first.next_cursor:
page = await client.list_tools(cursor=first.next_cursor, page_size=10) Defensive patterns
Strategy: fallback
Validate before calling
def is_valid_cursor(cursor: str | None) -> bool:
if cursor is None:
return True
import base64
try:
base64.b64decode(cursor, validate=True)
return True
except Exception:
return False
if not is_valid_cursor(cursor):
cursor = None # restart pagination from the first page Try / catch
try:
page = await client.list_tools(cursor=cursor, page_size=page_size)
except Exception as e:
if "INVALID_PARAMS" in str(e) or "cursor" in str(e).lower():
cursor = None # discard stale/invalid cursor, restart from page 1
page = await client.list_tools(page_size=page_size)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- Horizon returned an invalid organization cursor
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/d154c0f0b98abfbe.
Report an issue: GitHub.