{"record":{"id":"041321e420895c5b","repo":"Hmbown/CodeWhale","slug":"the-runtime-returned-a-model-page-for-a-different-provider","errorCode":null,"errorMessage":"The Runtime returned a model page for a different provider.","messagePattern":"The Runtime returned a model page for a different provider\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_web/app.mjs","lineNumber":667,"sourceCode":" */\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)\n      || response.total < 0\n      || response.total > MAX_PROVIDER_MODELS) {\n      throw new Error(\"The Runtime returned an invalid provider model page.\");\n    }\n    if (expectedTotal !== undefined && expectedTotal !== response.total) {\n      throw new Error(\"The provider catalog changed; restart loading its models.\");\n    }\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","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_web/app.mjs#L649-L685","documentation":"Each fetched page of a provider's model catalog must echo the provider id it was requested for. When response.provider does not match the requested provider, the runtime treats the catalog as corrupted/cross-wired (cursors and totals would be meaningless across providers) and throws rather than silently merging foreign models.","triggerScenarios":"The Runtime endpoint /v1/providers/<id>/models returns a body whose `provider` field is absent, different, or differently-cased/encoded than the requested id; a proxy/cache returns a cached page for another provider.","commonSituations":"Provider id casing/URL-encoding mismatch between request and response; misbehaving intermediate proxy serving stale pages; server bug mapping provider ids to catalogs.","solutions":["Log the requested provider and response.provider to see the mismatch (casing, encoding, or a different id entirely).","Bypass any proxy/cache and call the Runtime directly to rule out stale cached pages.","Fix or report the server-side provider-id normalization so the response echoes the exact requested id."],"exampleFix":"// before\nconst page = await fetchPage(`/v1/providers/${id}/models?...`);\n// after\nconst page = await fetchPage(`/v1/providers/${encodeURIComponent(id)}/models?...`);\nif (String(page?.provider || '') !== id) throw new Error('provider mismatch: ' + page?.provider);","handlingStrategy":"try-catch","validationCode":"const page = await fetchPage(url);\nif (String(page?.provider || '') !== providerId) {\n  throw new Error('page provider mismatch');\n}","typeGuard":"function isPageForProvider(response, providerId) {\n  return String(response?.provider || '') === providerId &&\n         Array.isArray(response?.models);\n}","tryCatchPattern":"try {\n  const models = await collectProviderModelPages(providerId, fetchPage);\n} catch (e) {\n  if (String(e.message).includes('different provider')) {\n    invalidateCatalogCache();\n    // refetch directly from the Runtime\n  } else throw e;\n}","preventionTips":["URL-encode provider ids consistently and compare case-sensitively.","Bypass caches/proxies when fetching provider catalogs, or key the cache on provider id.","Add a server-side test asserting the models response echoes the requested provider id."],"tags":["provider","pagination","response-validation"],"backgroundTag":"unexpected-response-shape","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"}