{"record":{"id":"b1e60a26d6c05ccc","repo":"Hmbown/CodeWhale","slug":"a-provider-and-page-loader-are-required","errorCode":null,"errorMessage":"A provider and page loader are required.","messagePattern":"A provider and page loader are required\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_web/app.mjs","lineNumber":653,"sourceCode":"}\n\nconst PROVIDER_MODELS_PAGE_SIZE = 250;\nconst MAX_PROVIDER_MODELS = 10_000;\nconst MAX_PROVIDER_MODEL_PAGES = Math.ceil(\n  MAX_PROVIDER_MODELS / PROVIDER_MODELS_PAGE_SIZE,\n);\n\n/**\n * Load every bounded page of one provider catalog.\n *\n * `fetchPage` is injected so the browser client can retain its authenticated\n * Runtime API boundary and tests can prove catalogs larger than one page are\n * not silently truncated. Cursors are opaque and may never repeat.\n */\nexport async function collectProviderModelPages(providerId, fetchPage) {\n  const provider = String(providerId || \"\").trim();\n  if (!provider || typeof fetchPage !== \"function\") {\n    throw new Error(\"A provider and page loader are required.\");\n  }\n\n  const entries = [];\n  const seenCursors = new Set();\n  let expectedTotal;\n  let cursor = \"\";\n  for (let page = 0; page < MAX_PROVIDER_MODEL_PAGES; page += 1) {\n    const query = new URLSearchParams({ limit: String(PROVIDER_MODELS_PAGE_SIZE) });\n    if (cursor) query.set(\"cursor\", cursor);\n    const response = await fetchPage(\n      `/v1/providers/${encodeURIComponent(provider)}/models?${query.toString()}`,\n    );\n    if (String(response?.provider || \"\") !== provider) {\n      throw new Error(\"The Runtime returned a model page for a different provider.\");\n    }\n    if (!Array.isArray(response?.models)\n      || response.models.length > PROVIDER_MODELS_PAGE_SIZE\n      || !Number.isSafeInteger(response.total)","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_web/app.mjs#L635-L671","documentation":"collectProviderModelPages paginates a provider's model catalog and requires a non-empty provider id plus a callable fetchPage loader. Anything else is a caller bug (it cannot know which provider's pages to fetch or how to fetch them), so it throws up front.","triggerScenarios":"Calling collectProviderModelPages('') or with a non-function second argument (undefined when an optional dependency failed to load, or a value mistakenly passed instead of a function).","commonSituations":"Refactoring left the fetch callback undefined; provider id lost from scope and passed as empty string; tests or callers invoking the helper directly with wrong arity.","solutions":["Pass a non-empty provider id string as the first argument.","Pass the actual page-loader function (e.g. (url) => fetch(url).then(r => r.json())) as the second argument.","Check the import of the loader function if it comes from another module (name typo yields undefined)."],"exampleFix":"// before\nconst models = await collectProviderModelPages(providerId, loaderUrl);\n// after\nconst models = await collectProviderModelPages(providerId, (url) => fetch(url).then((r) => r.json()));","handlingStrategy":"validation","validationCode":"if (providerId && typeof fetchPage === 'function') {\n  const pages = await collectProviderModelPages(providerId, fetchPage);\n}","typeGuard":"function canCollect(providerId, fetchPage) {\n  return String(providerId || '').trim().length > 0 && typeof fetchPage === 'function';\n}","tryCatchPattern":"try {\n  const pages = await collectProviderModelPages(providerId, fetchPage);\n} catch (e) {\n  console.error('collectProviderModelPages misused:', e.message);\n}","preventionTips":["Assert loader arguments with unit tests for the pagination helper.","Import the loader by name and verify it is not undefined at call time.","Keep provider id and loader wiring together in one call site."],"tags":["validation","pagination","provider"],"backgroundTag":"invalid-argument-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}