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

  1. Configure the server to return its whole catalog in one page (raise/infinite page size)
  2. Follow the cursor by issuing tools/list calls with the cursor param until nextCursor is null, then relax or extend the probe
  3. Check server settings/policy for tool pagination limits
  4. 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

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


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/8ee3a184b5c89aca. Report an issue: GitHub.