{"record":{"id":"6eaac74fb558f7b7","repo":"Hmbown/CodeWhale","slug":"mcp-exceeded-the-byte-aggregate-catalog-limit","errorCode":null,"errorMessage":"MCP {} exceeded the {}-byte aggregate catalog limit","messagePattern":"MCP (.+?) exceeded the (.+?)-byte aggregate catalog limit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":383,"sourceCode":"        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            );\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!(","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/stdio_client.rs#L365-L401","documentation":"observe_page accumulates the serialized size in bytes of every page received. When the aggregate exceeds max_bytes the listing fails rather than returning partial results, protecting memory and context budgets from oversized server catalogs.","triggerScenarios":"list_paginated's running byte counter (self.bytes) exceeds max_bytes after recording a page, typically with large entries (long descriptions, embedded content) even at modest page counts.","commonSituations":"MCP server with verbose resource descriptions or huge tool schemas; max_bytes tuned too low; a server echoing bloated metadata.","solutions":["Increase the max_bytes aggregate budget","Trim descriptions/metadata exposed by the MCP server","Reduce items per page via server-side filtering"],"exampleFix":"// before\nmax_bytes: 256 * 1024\n// after\nmax_bytes: 1024 * 1024","handlingStrategy":"try-catch","validationCode":"// estimate payload: items x avg entry bytes\nlet est_bytes = expected_items * avg_entry_bytes;\nassert!(max_bytes > est_bytes, \"max_bytes below expected payload\");","typeGuard":null,"tryCatchPattern":"match client.list_resources_with_metadata().await {\n    Ok(entries) => use(entries),\n    Err(e) if e.to_string().contains(\"-byte aggregate catalog limit\") => {\n        eprintln!(\"catalog payload exceeds byte budget: {}\", e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep server descriptions/schemas concise","Budget max_bytes from measured page sizes, not guesses","Alert when a server's serialized catalog size grows sharply"],"tags":["mcp","limits","memory"],"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"}