different-ai/openwork · error · SafeProbeFailure
invalid_catalog
invalid_catalog
Error message
invalid_catalog
What it means
validateCatalog throws SafeProbeFailure("invalid_catalog") at line 550 when the tools/list result's 'tools' member is not an array or its length exceeds MAX_TOOL_COUNT. The probe caps catalog size both as a sanity check and a resource guard, and requires the field to be a proper array.
Source
Thrown at apps/server/src/agent-context-cloud-probe.ts:550
if (payload.id !== requestId) throw new SafeProbeFailure("request_id_mismatch");
if (!isRecord(payload.result)) throw new SafeProbeFailure("invalid_jsonrpc_envelope");
return payload.result;
}
function requireSupportedProtocolVersion(initializeResult: Record<string, unknown>): void {
const version = initializeResult.protocolVersion;
if (typeof version !== "string" || version.length === 0 || version.length > MAX_PROTOCOL_HEADER_LENGTH) {
throw new SafeProbeFailure("invalid_jsonrpc_envelope");
}
if (version !== MCP_PROTOCOL_VERSION) throw new SafeProbeFailure("unsupported_protocol_version");
}
function validateCatalog(rpcResult: Record<string, unknown>): { toolIds: string[]; totalToolCount: number } {
if (rpcResult.nextCursor !== undefined && rpcResult.nextCursor !== null) {
throw new SafeProbeFailure("pagination_unsupported");
}
if (!Array.isArray(rpcResult.tools) || rpcResult.tools.length > MAX_TOOL_COUNT) {
throw new SafeProbeFailure("invalid_catalog");
}
const seen = new Set<string>();
for (const tool of rpcResult.tools) {
if (!isRecord(tool) || typeof tool.name !== "string" || tool.name.length > MAX_TOOL_ID_LENGTH || !TOOL_ID.test(tool.name)) {
throw new SafeProbeFailure("invalid_catalog");
}
if (seen.has(tool.name)) throw new SafeProbeFailure("invalid_catalog");
seen.add(tool.name);
}
// Additional provider tools are forward-compatible and allowed, but never
// reflect provider-controlled catalog names into the diagnostic report. In
// particular, a compromised trusted endpoint must not be able to echo the
// bearer token back as a syntactically valid tool identifier. Only the
// expected allowlisted IDs and the aggregate count are exported.
const missing = REQUIRED_TOOL_IDS.filter((toolId) => !seen.has(toolId));
if (missing.length > 0) throw new CatalogRequiredToolsFailure(rpcResult.tools.length);
return { toolIds: [...REQUIRED_TOOL_IDS], totalToolCount: rpcResult.tools.length };
}View on GitHub (pinned to 2b7df46e8a)
Solutions
- Fix the server so tools/list returns { tools: [...] } as an array
- Reduce the number of registered/exposed tools below MAX_TOOL_COUNT (trim plugins or integrations)
- Raise the probe's MAX_TOOL_COUNT only via a deliberate client change
- Inspect the raw tools/list body to see the actual shape and size
Example fix
// before
{"result":{"tools":{"myTool":{...}}}}
// after
{"result":{"tools":[{"name":"myTool",...}]}} Defensive patterns
Strategy: validation
Validate before calling
const result = JSON.parse(raw)?.result;
if (!result || !Array.isArray(result.tools)) throw new Error("tools/list result.tools must be an array");
if (result.tools.length > MAX_TOOL_COUNT) throw new Error(`catalog too large: ${result.tools.length} > ${MAX_TOOL_COUNT}`); Type guard
function hasValidToolsArray(result: Record<string, unknown>, max: number): result is { tools: unknown[] } & Record<string, unknown> {
return Array.isArray(result.tools) && result.tools.length <= max;
} Try / catch
try {
const catalog = validateCatalog(rpcResult);
} catch (e) {
if (e instanceof SafeProbeFailure && e.code === "invalid_catalog") {
console.error("tools/list shape or size rejected", { isArray: Array.isArray(rpcResult.tools), count: rpcResult.tools?.length });
} else throw e;
} Prevention
- Contract-test tools/list response shape on every server release
- Keep exposed tool counts under the client's MAX_TOOL_COUNT cap (disable unused plugins)
- Inspect the raw tools/list body before assuming a client bug
When it happens
Trigger: Server returns tools as null/object/string, omits tools entirely, or returns more tools than MAX_TOOL_COUNT.
Common situations: Nonconformant MCP server with a different tools/list response shape; a server whose catalog legitimately exceeds the probe's safety cap (very large tool registries); middleware injecting extra tool entries.
Related errors
- pagination_unsupported
- MCP_TOOL_DISCOVERY
- MCP_PROVIDER_INVALID_PARAMS
- invalid_mcp_token_payload
- invalid_mcp_connection_payload
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/9e70362e30a72160.
Report an issue: GitHub.