different-ai/openwork · error · SafeProbeFailure
pagination_unsupported
pagination_unsupported
Error message
pagination_unsupported
What it means
validateCatalog throws SafeProbeFailure("pagination_unsupported") when the tools/list result contains a non-null nextCursor, meaning the server paginates its tool catalog. The probe fetches only one page and cannot enumerate all tools, so it refuses rather than under-reporting the catalog.
Source
Thrown at apps/server/src/agent-context-cloud-probe.ts:547
throw new SafeProbeFailure("invalid_jsonrpc_envelope");
}
if (Object.hasOwn(payload, "error")) throw new SafeProbeFailure("jsonrpc_error");
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));View on GitHub (pinned to 2b7df46e8a)
Solutions
- Configure the server to return its whole catalog in one page (raise/infinite page size)
- Follow the cursor by issuing tools/list calls with the cursor param until nextCursor is null, then relax or extend the probe
- Check server settings/policy for tool pagination limits
- If pagination is intended, the probe itself needs a cursor-following loop added
Example fix
// before (server)
{"tools":[...],"nextCursor":"page-2"}
// after
{"tools":[...all tools...]} Defensive patterns
Strategy: try-catch
Validate before calling
const body = JSON.parse(raw);
const cursor = (body?.result as Record<string, unknown> | undefined)?.nextCursor;
if (cursor !== undefined && cursor !== null) {
throw new Error(`server paginates tools/list (nextCursor=${String(cursor)}); single-page probe not supported`);
} Type guard
function isUnpaginatedToolsResult(result: Record<string, unknown>): boolean {
return result.nextCursor === undefined || result.nextCursor === null;
} Try / catch
try {
const { toolIds, totalToolCount } = validateCatalog(rpcResult);
} catch (e) {
if (e instanceof SafeProbeFailure && e.code === "pagination_unsupported") {
// either reconfigure the server to a single page or implement cursor-following tools/list paging
} else throw e;
} Prevention
- Configure servers with large catalogs to disable tool pagination or raise page size above tool count
- Probe server behavior once at onboarding and record whether it paginates
- If pagination is expected, implement cursor-following before relying on single-shot probes
When it happens
Trigger: Server's tools/list response includes nextCursor (string cursor) because its tool count exceeds one page or it paginates unconditionally.
Common situations: Large MCP servers exposing hundreds of tools that paginate by default; server implementations that always set nextCursor even for full pages; recent server upgrade introducing pagination where none existed before.
Related errors
- MCP_TOOL_DISCOVERY
- invalid_catalog
- MCP_CATALOG_CURSOR_SIZE_LIMIT
- MCP_CATALOG_CURSOR_LOOP
- MCP_CATALOG_PAGE_LIMIT
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/8ee3a184b5c89aca.
Report an issue: GitHub.