{"record":{"id":"135ceac8c59517c1","repo":"Hmbown/CodeWhale","slug":"mcp-exceeded-the-page-catalog-limit","errorCode":null,"errorMessage":"MCP {} exceeded the {}-page catalog limit","messagePattern":"MCP (.+?) exceeded the (.+?)-page catalog limit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":369,"sourceCode":"                self.overall_timeout\n            );\n        }\n        Ok(remaining)\n    }\n\n    fn observe_page(&mut self, page: &Value, field: &str) -> Result<Option<String>> {\n        let values = page.get(field).and_then(Value::as_array).with_context(|| {\n            format!(\n                \"MCP {} response did not contain a '{field}' array\",\n                self.method\n            )\n        })?;\n\n        self.pages = self.pages.saturating_add(1);\n        self.items = self.items.saturating_add(values.len());\n        self.bytes = self.bytes.saturating_add(serde_json::to_vec(page)?.len());\n        if self.pages > self.max_pages {\n            bail!(\n                \"MCP {} exceeded the {}-page catalog limit\",\n                self.method,\n                self.max_pages\n            );\n        }\n        if self.items > self.max_items {\n            bail!(\n                \"MCP {} exceeded the {}-item catalog limit\",\n                self.method,\n                self.max_items\n            );\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            );","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/stdio_client.rs#L351-L387","documentation":"observe_page enforces a bounded budget on catalog pagination. After recording a page, if the page count exceeds max_pages the operation fails rather than returning a partial catalog. This is a deliberate fail-loud guard against runaway or looping servers.","triggerScenarios":"list_paginated receives a nextCursor for (max_pages+1)-th fetch: pages counter already reached max_pages and the server still advertises more data.","commonSituations":"Misbehaving MCP server with huge or unbounded resource/prompt/tool lists; cursor loop with fresh cursors; max_pages configured too low for a legitimately large catalog.","solutions":["Raise the max_pages catalog budget for this server","Fix/upgrade the MCP server if it paginates abnormally or loops","Filter the catalog server-side to shrink the result set"],"exampleFix":"// before\nbudget: CatalogBudget::new(method, deadline, /*max_pages*/ 10, ...)\n// after\nbudget: CatalogBudget::new(method, deadline, /*max_pages*/ 50, ...)","handlingStrategy":"try-catch","validationCode":"// no pre-call check possible; keep budgets above the known catalog page count\nassert!(max_pages >= server_page_count_estimate);","typeGuard":null,"tryCatchPattern":"match client.list_resources_with_metadata().await {\n    Ok(entries) => use(entries),\n    Err(e) if e.to_string().contains(\"-page catalog limit\") => {\n        eprintln!(\"server catalog exceeds page budget: {}\", e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Set max_pages from measured server pagination behavior","Watch for servers whose page counts grow across releases","Investigate cursor behavior if page counts balloon unexpectedly (may mask a loop)"],"tags":["mcp","pagination","limits"],"backgroundTag":"payload-too-large","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}