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

  1. Restart the load after the catalog settles so the first page's `total` is accurate
  2. Check the Runtime cursor logic for an off-by-one that emits a cursor past the last page
  3. Deduplicate provider model ids so entries.length tracks the true count
  4. 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

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


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)