{"record":{"id":"d99cf5e8fd257f78","repo":"Hmbown/CodeWhale","slug":"mcp-repeated-a-pagination-cursor","errorCode":null,"errorMessage":"MCP {} repeated a pagination cursor","messagePattern":"MCP (.+?) repeated a pagination cursor","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":398,"sourceCode":"            );\n        }\n        if self.bytes > self.max_bytes {\n            bail!(\n                \"MCP {} exceeded the {}-byte aggregate catalog limit\",\n                self.method,\n                self.max_bytes\n            );\n        }\n\n        let next_cursor = match page.get(\"nextCursor\") {\n            None => None,\n            Some(Value::String(cursor)) => Some(cursor.clone()),\n            Some(_) => bail!(\"MCP {} returned a non-string nextCursor\", self.method),\n        };\n        if let Some(cursor) = next_cursor.as_ref()\n            && !self.seen_cursors.insert(cursor.clone())\n        {\n            bail!(\"MCP {} repeated a pagination cursor\", self.method);\n        }\n        if next_cursor.is_some() && self.pages >= self.max_pages {\n            bail!(\n                \"MCP {} exceeded the {}-page catalog limit\",\n                self.method,\n                self.max_pages\n            );\n        }\n        Ok(next_cursor)\n    }\n}\n\n/// What the server said it supports in its `initialize` response.\n///\n/// `None` means the server sent no `capabilities` object at all. Those are\n/// treated as legacy servers and probed optimistically; an explicit\n/// capabilities object is honoured, because a tools-only server answers\n/// `resources/list` with a \"method not found\" error that would otherwise fail","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/stdio_client.rs#L380-L416","documentation":"observe_page records every nextCursor in a seen_cursors set. If the server returns a cursor that was already followed, the listing would loop forever, so it fails immediately with this error instead of spinning or returning partial results.","triggerScenarios":"list_paginated receives a nextCursor identical to one previously returned by the same listing operation (set insertion returns false).","commonSituations":"Buggy server pagination that re-emits the same cursor; cursor derived from a stable offset that never advances; server restart mid-listing resetting cursor state.","solutions":["Fix/upgrade the MCP server so each nextCursor advances the pagination state","Avoid a proxy that strips pagination parameters and replays the same request","Report the server's loop; hardcode a smaller catalog or skip listing for that server"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// client-side: detect cursor repetition before the library does\nlet mut seen = std::collections::HashSet::new();\n// on each page: if !seen.insert(cursor.clone()) { /* server is looping */ }","typeGuard":null,"tryCatchPattern":"match client.list_resources_with_metadata().await {\n    Ok(entries) => use(entries),\n    Err(e) if e.to_string().contains(\"repeated a pagination cursor\") => {\n        eprintln!(\"MCP server pagination loop detected; filing bug against server\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Ensure server cursors strictly advance pagination state","Never reuse a cursor value within one listing session","Add server integration tests that walk the full cursor chain"],"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-22T21:17:16.096Z"}