different-ai/openwork · error · ExternalMcpDiagnosticError
MCP_CATALOG_CURSOR_LOOP
MCP_CATALOG_CURSOR_LOOP
Error message
MCP_CATALOG_CURSOR_LOOP
What it means
Thrown when tools/list pagination returns a nextCursor that was already seen in this collection pass, indicating the provider's pagination is looping and will never terminate. The gateway detects this via the seenCursors set and aborts instead of looping forever.
Source
Thrown at ee/apps/den-api/src/capability-sources/external-mcp-client.ts:735
})
}
const cursorMeasurement = measureSerializedJson(
result.nextCursor,
EXTERNAL_MCP_CATALOG_LIMIT_BYTES - catalogBytes,
1,
)
if (!cursorMeasurement.ok) {
throw catalogDiagnosticError({
tracker: input.diagnostic,
code: "MCP_CATALOG_BYTE_LIMIT",
operatorAction: `Reduce the complete serialized tool catalog below ${EXTERNAL_MCP_CATALOG_LIMIT_BYTES} bytes.`,
})
}
catalogBytes += cursorMeasurement.bytes
if (seenCursors.has(result.nextCursor)) {
throw catalogDiagnosticError({
tracker: input.diagnostic,
code: "MCP_CATALOG_CURSOR_LOOP",
operatorAction: "Fix the provider's tools/list pagination so each nextCursor advances.",
})
}
seenCursors.add(result.nextCursor)
cursor = result.nextCursor
}
throw catalogDiagnosticError({
tracker: input.diagnostic,
code: "MCP_CATALOG_PAGE_LIMIT",
operatorAction: `Reduce the provider catalog to at most ${pageLimit} pages or use a scoped MCP server.`,
})
}
export type ExternalMcpConnectResult =
| { status: "connected" }
| { status: "needs_auth"; authorizeUrl: string }
/**View on GitHub (pinned to 2b7df46e8a)
Solutions
- Fix the provider's tools/list so each nextCursor advances to new results
- Verify the server actually applies the cursor argument when filtering results
- Check for caching layers serving a stale first page for every cursor
- If a middle page cursor repeats due to a server bug, report it to the provider operator
Example fix
// before: handler ignores cursor, always returns nextCursor "p:2" // after: derive nextCursor from the served page, e.g. offset += pageSize
Defensive patterns
Strategy: retry
Validate before calling
const seen = new Set<string>()
let cursor: string | undefined
while (cursor !== undefined) {
if (seen.has(cursor)) throw new Error("pagination loop detected: repeated nextCursor")
seen.add(cursor)
cursor = (await listPage(cursor)).nextCursor
} Try / catch
try {
await connectExternalMcp(...)
} catch (err) {
if (err instanceof Error && err.message.includes("MCP_CATALOG_CURSOR_LOOP")) {
console.error("Provider pagination loops; fix tools/list nextCursor advancement before retrying")
// do not blind-retry: the loop is deterministic until the server is fixed
} else throw err
} Prevention
- Ensure your server derives nextCursor from the served page, not a constant
- Integration-test full pagination of your tools/list
- Check caching layers for stale first-page responses
- Log cursor values server-side to spot non-advancing sequences
When it happens
Trigger: seenCursors.has(result.nextCursor) is true when the next page's cursor is generated — i.e. the provider handed back a cursor identical to one already consumed.
Common situations: Broken server pagination that ignores the cursor and returns page 1's next cursor forever; cursors that encode a filter the server does not actually apply; stale/cached pages; static nextCursor pointing at a page that returns itself.
Related errors
- MCP_TOOL_DISCOVERY
- MCP_CATALOG_CURSOR_SIZE_LIMIT
- pagination_unsupported
- MCP_CATALOG_PAGE_LIMIT
- invalid_mcp_token_payload
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/3a8ca4d2f8555714.
Report an issue: GitHub.