{"record":{"id":"ef57bf60389092f2","repo":"Hmbown/CodeWhale","slug":"the-runtime-returned-a-non-progressing-model-cursor","errorCode":null,"errorMessage":"The Runtime returned a non-progressing model cursor.","messagePattern":"The Runtime returned a non-progressing model cursor\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_web/app.mjs","lineNumber":696,"sourceCode":"    }\n    expectedTotal = response.total;\n    const pageEntries = response.models;\n    if (entries.length + pageEntries.length > MAX_PROVIDER_MODELS) {\n      throw new Error(`The provider catalog exceeds ${MAX_PROVIDER_MODELS} models.`);\n    }\n    entries.push(...pageEntries);\n\n    const nextCursor = typeof response?.nextCursor === \"string\"\n      ? response.nextCursor.trim()\n      : \"\";\n    if (!nextCursor) {\n      if (entries.length !== expectedTotal) {\n        throw new Error(\"The Runtime returned an incomplete provider model catalog.\");\n      }\n      return entries;\n    }\n    if (pageEntries.length === 0 || seenCursors.has(nextCursor)) {\n      throw new Error(\"The Runtime returned a non-progressing model cursor.\");\n    }\n    if (entries.length >= expectedTotal) {\n      throw new Error(\"The Runtime returned a cursor beyond its provider model catalog.\");\n    }\n    seenCursors.add(nextCursor);\n    cursor = nextCursor;\n  }\n  throw new Error(`The provider catalog exceeds ${MAX_PROVIDER_MODELS} models.`);\n}\n\nfunction startBrowserClient() {\n  const dom = {\n    shell: document.querySelector(\"#app-shell\"),\n    rail: document.querySelector(\"#thread-rail\"),\n    railOpen: document.querySelector(\"#rail-open\"),\n    railClose: document.querySelector(\"#rail-close\"),\n    railScrim: document.querySelector(\"#rail-scrim\"),\n    search: document.querySelector(\"#thread-search\"),","sourceCodeStart":678,"sourceCodeEnd":714,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_web/app.mjs#L678-L714","documentation":"Thrown when a page yields an empty entries array while still returning a cursor, or when the returned cursor was already visited. Pagination must strictly progress; either condition means an infinite loop would occur, so the loader aborts.","triggerScenarios":"Runtime returns {models: [], nextCursor: \"x\"} or re-issues a cursor previously passed in seenCursors — typically a cursor implementation that doesn't advance (e.g. cursor encoding the same offset repeatedly).","commonSituations":"A Runtime build with a broken cursor codec; a provider adapter that ignores the cursor parameter and always returns the same page; caching middleware replaying an identical response for successive cursor requests.","solutions":["Restart `codewhale web` with a current Runtime build (fixed cursor logic)","Bypass intermediate caches so each cursor request reaches the Runtime","Verify the provider adapter honors the cursor/offset parameter","Reload the dialog to restart pagination; if it recurs, file a Runtime bug with the cursor values"],"exampleFix":"// before: looping on the same cursor until the cap trips\nwhile (nextCursor) { page = await fetch('?cursor=' + nextCursor); }\n// after: detect non-progress like the loader does\nif (!page.models.length || visited.has(page.nextCursor)) throw new Error('non-progressing model cursor');","handlingStrategy":"validation","validationCode":"const visited = new Set(); if (visited.has(page.nextCursor) || !page.models.length) abortLoad();","typeGuard":"const cursorProgresses = (page, seen) => Array.isArray(page?.models) && page.models.length > 0 && !seen.has(page?.nextCursor);","tryCatchPattern":"try { models = await loadCatalog(); } catch (e) { if (String(e.message).includes('non-progressing')) reportRuntimeBug(e); }","preventionTips":["Keep Runtime cursor implementations advancing monotonically","Bypass caches for cursor-paginated requests","Test provider adapters honor cursor parameters","Abort pagination on the first repeated cursor instead of looping"],"tags":["pagination","infinite-loop","cursor"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}