Hmbown/CodeWhale · error
The Runtime returned a cursor beyond its provider model…
Error message
The Runtime returned a cursor beyond its provider model catalog.
What it means
Thrown when the Runtime returns another cursor even though the loader has already accumulated entries.length >= expectedTotal models. The catalog said it had N models; once N are collected there must be no further pages. Continuing would collect duplicates beyond the declared catalog.
Solutions
- Restart the load after the catalog settles so the first page's `total` is accurate
- Check the Runtime cursor logic for an off-by-one that emits a cursor past the last page
- Deduplicate provider model ids so entries.length tracks the true count
- Update/restart the Runtime if it is an older build with a known cursor bug
Example fix
// before: appending pages while a cursor exists, ignoring total
while (true) { const page = await fetchPage(cursor); entries.push(...page.models); if (!page.nextCursor) break; cursor = page.nextCursor; }
// after: stop when the declared total is reached
if (entries.length >= expectedTotal && page.nextCursor) throw new Error('cursor beyond catalog'); Defensive patterns
Strategy: validation
Validate before calling
if (entries.length >= expectedTotal && page.nextCursor) throw new Error('cursor beyond catalog'); Type guard
const withinCatalog = (entries, total, page) => entries.length < total || !page?.nextCursor;
Try / catch
try { models = await loadCatalog(); } catch (e) { if (String(e.message).includes('beyond')) await reloadCatalog(); } Prevention
- Re-fetch `total` if the catalog may have grown mid-load
- Deduplicate model ids so counts stay accurate
- Fix off-by-one trailing cursors in the Runtime
- Stop paginating once the declared total is reached
When it happens
Trigger: expectedTotal undercounts the real catalog size (or pages return duplicates), so after collecting `total` entries the Runtime still supplies a nextCursor.
Common situations: Provider added models between the first page (which fixed total) and later pages; a Runtime cursor implementation with an off-by-one that emits a trailing cursor; duplicate model ids inflating entries past the true count.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- The provider catalog changed; restart loading its models.
- The provider catalog exceeds
- The Runtime returned a non-progressing model cursor.
- The Runtime returned an incomplete provider model catalog.
- The Runtime returned an invalid provider model page.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e01f888a39e17228.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_web/app.mjs:699
if (entries.length + pageEntries.length > MAX_PROVIDER_MODELS) {
throw new Error(`The provider catalog exceeds ${MAX_PROVIDER_MODELS} models.`);
}
entries.push(...pageEntries);
const nextCursor = typeof response?.nextCursor === "string"
? response.nextCursor.trim()
: "";
if (!nextCursor) {
if (entries.length !== expectedTotal) {
throw new Error("The Runtime returned an incomplete provider model catalog.");
}
return entries;
}
if (pageEntries.length === 0 || seenCursors.has(nextCursor)) {
throw new Error("The Runtime returned a non-progressing model cursor.");
}
if (entries.length >= expectedTotal) {
throw new Error("The Runtime returned a cursor beyond its provider model catalog.");
}
seenCursors.add(nextCursor);
cursor = nextCursor;
}
throw new Error(`The provider catalog exceeds ${MAX_PROVIDER_MODELS} models.`);
}
function startBrowserClient() {
const dom = {
shell: document.querySelector("#app-shell"),
rail: document.querySelector("#thread-rail"),
railOpen: document.querySelector("#rail-open"),
railClose: document.querySelector("#rail-close"),
railScrim: document.querySelector("#rail-scrim"),
search: document.querySelector("#thread-search"),
threadList: document.querySelector("#thread-list"),
newThread: document.querySelector("#new-thread"),
newThreadDialog: document.querySelector("#new-thread-dialog"),View on GitHub (pinned to 73e0f67d83)