{"record":{"id":"2b5a20373e7b1a16","repo":"google-gemini/gemini-cli","slug":"model-policy-chain-must-include-at-least-one-model","errorCode":null,"errorMessage":"Model policy chain must include at least one model.","messagePattern":"Model policy chain must include at least one model\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/core/src/availability/policyCatalog.ts","lineNumber":154,"sourceCode":"}\n\nexport function getFlashLitePolicyChain(): ModelPolicyChain {\n  return cloneChain(FLASH_LITE_CHAIN);\n}\n\n/**\n * Provides a default policy scaffold for models not present in the catalog.\n */\nexport function createDefaultPolicy(\n  model: string,\n  options?: { isLastResort?: boolean },\n): ModelPolicy {\n  return definePolicy({ model, isLastResort: options?.isLastResort });\n}\n\nexport function validateModelPolicyChain(chain: ModelPolicyChain): void {\n  if (chain.length === 0) {\n    throw new Error('Model policy chain must include at least one model.');\n  }\n  const lastResortCount = chain.filter((policy) => policy.isLastResort).length;\n  if (lastResortCount === 0) {\n    throw new Error('Model policy chain must include an `isLastResort` model.');\n  }\n  if (lastResortCount > 1) {\n    throw new Error('Model policy chain must only have one `isLastResort`.');\n  }\n}\n\n/**\n * Helper to define a ModelPolicy with default actions and state transitions.\n * Ensures every policy is a fresh instance to avoid shared state.\n */\nfunction definePolicy(config: PolicyConfig): ModelPolicy {\n  return {\n    model: config.model,\n    isLastResort: config.isLastResort,","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/core/src/availability/policyCatalog.ts#L136-L172","documentation":"Thrown by validateModelPolicyChain() when the chain array has length 0. The model policy chain is an ordered list of ModelPolicy entries that the availability subsystem walks to find a usable model; each entry defines a model ID, retry behavior, and whether it is a last-resort fallback. An empty chain means the system has no model to try at all, which would cause undefined behavior downstream.","triggerScenarios":"Calling validateModelPolicyChain([]) — passing an empty array. This typically happens when a chain builder function (e.g., createAutoRoutingChain or a custom one) returns [] due to a logic error, or when a configuration override empties the chain.","commonSituations":"A custom chain builder returns an empty array under an unhandled configuration branch; filtering a chain to remove all entries (e.g., filtering out unavailable models); a config migration or settings.json edit that accidentally sets an empty model list; programmatic chain construction that pushes into an array conditionally and all conditions are false.","solutions":["Ensure the chain builder always returns at least one ModelPolicy entry; use createDefaultPolicy(model, { isLastResort: true }) as a guaranteed fallback.","If filtering models, always append a last-resort policy after filtering so the chain is never empty.","Add a guard after building: if (chain.length === 0) chain = [createSingleModelChain(DEFAULT_GEMINI_MODEL)].","Validate configuration overrides that affect model selection to confirm they produce non-empty chains."],"exampleFix":"// before — filter can produce empty array\nconst chain = catalog\n  .filter((m) => m.available)\n  .map((m) => createDefaultPolicy(m.id));\nvalidateModelPolicyChain(chain); // throws if all unavailable\n\n// after — guarantee a last-resort fallback\nconst chain = catalog\n  .filter((m) => m.available)\n  .map((m) => createDefaultPolicy(m.id));\nif (chain.length === 0) {\n  chain.push(createDefaultPolicy(DEFAULT_GEMINI_MODEL, { isLastResort: true }));\n}","handlingStrategy":"validation","validationCode":"// Ensure chain is never empty before validation\nif (chain.length === 0) {\n  chain = [createSingleModelChain(DEFAULT_GEMINI_MODEL)];\n}\nvalidateModelPolicyChain(chain);","typeGuard":"function isNonEmptyChain(chain: ModelPolicyChain): chain is [ModelPolicy, ...ModelPolicy[]] {\n  return chain.length > 0;\n}","tryCatchPattern":"try {\n  validateModelPolicyChain(chain);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('must include at least one model')) {\n    // Fall back to a safe default chain\n    chain = [createSingleModelChain(DEFAULT_GEMINI_MODEL)];\n    validateModelPolicyChain(chain);\n  } else throw e;\n}","preventionTips":["Always use createSingleModelChain or built-in chain builders as a fallback.","After any filter operation on a chain, assert the result is non-empty.","Add a unit test that every chain builder returns at least one entry.","Validate chains at config load time, not just at request time."],"tags":["model-policy","availability","validation","configuration"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}