{"record":{"id":"42aaa582abeb2f1a","repo":"Hmbown/CodeWhale","slug":"repeated-pagination-cursor-aborting","errorCode":null,"errorMessage":"{} repeated pagination cursor; aborting","messagePattern":"(.+?) repeated pagination cursor; aborting","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp.rs","lineNumber":1518,"sourceCode":"                self.method,\n                MAX_MCP_CATALOG_ITEMS\n            );\n        }\n        if self.bytes > MAX_MCP_CATALOG_BYTES {\n            anyhow::bail!(\n                \"{} exceeded the {}-byte aggregate catalogue limit\",\n                self.method,\n                MAX_MCP_CATALOG_BYTES\n            );\n        }\n        let cursor = result\n            .get(\"nextCursor\")\n            .and_then(|value| value.as_str())\n            .map(str::to_owned);\n        if let Some(cursor) = cursor.as_ref()\n            && !self.seen_cursors.insert(cursor.clone())\n        {\n            anyhow::bail!(\"{} repeated pagination cursor; aborting\", self.method);\n        }\n        Ok(cursor)\n    }\n}\n\nfn is_legacy_sse_transport(config: &McpServerConfig) -> bool {\n    config\n        .transport\n        .as_deref()\n        .map(|transport| transport.trim().eq_ignore_ascii_case(\"sse\"))\n        .unwrap_or(false)\n}\n\npub fn validate_mcp_transport(transport: Option<&str>) -> Result<()> {\n    let Some(transport) = transport else {\n        return Ok(());\n    };\n    if transport.trim().eq_ignore_ascii_case(\"sse\") {","sourceCodeStart":1500,"sourceCodeEnd":1536,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp.rs#L1500-L1536","documentation":"During paginated catalogue discovery the client records every nextCursor the server returns in a seen_cursors set. If the server returns a cursor that was already seen, pagination would loop forever, so the client bails out. This guards against broken MCP servers that keep replaying the same page.","triggerScenarios":"Calling tools/list (or resource list) pagination against an MCP server whose nextCursor values repeat — the server ignores the cursor argument and returns page 1 forever, or returns a fixed cursor.","commonSituations":"Buggy or non-conformant MCP server implementations; proxies/caches replaying list responses; server-side pagination bugs after an upgrade.","solutions":["Fix or upgrade the MCP server so it advances pagination correctly","Restart the MCP server — a stale in-memory pager can replay cursors","Bypass pagination by reducing the catalogue so it fits in one page","Check for caching middlewares between the client and server and disable them"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// probe pagination before long discovery: fetch one page and check the cursor differs from the request cursor\nlet probe = list_tools(None).await?;\nif probe.next_cursor.as_deref() == Some(\"\") && probe.tools.len() == page_limit { /* suspect broken pager */ }","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"repeated pagination cursor\") => {\n        // fall back to single-page listing or restart server\n    }\n    other => other?,\n}","preventionTips":["Upgrade MCP server implementations to conformant versions","Test pagination after server upgrades","Avoid caching proxies on MCP endpoints"],"tags":["mcp","pagination","infinite-loop"],"backgroundTag":"unexpected-response-shape","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}