{"record":{"id":"cfdf8f81485046e1","repo":"google-gemini/gemini-cli","slug":"model-policy-chain-must-include-an-islastresort","errorCode":null,"errorMessage":"Model policy chain must include an `isLastResort` model.","messagePattern":"Model policy chain must include an `isLastResort` model\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/core/src/availability/policyCatalog.ts","lineNumber":158,"sourceCode":"}\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,\n    maxAttempts: config.maxAttempts,\n    actions: { ...DEFAULT_ACTIONS, ...(config.actions ?? {}) },\n    stateTransitions: {\n      ...DEFAULT_STATE,","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/core/src/availability/policyCatalog.ts#L140-L176","documentation":"Thrown by validateModelPolicyChain() when the chain is non-empty but no entry has isLastResort set to true. The last-resort model is the terminal fallback the system uses when all other models in the chain have exhausted their maxAttempts. Without one, the availability loop could fail without a guaranteed recovery path, leaving the user with no model to serve requests.","triggerScenarios":"Calling validateModelPolicyChain(chain) where chain has entries but none have isLastResort: true. For example, [{ model: 'gemini-pro', isLastResort: false }, { model: 'gemini-flash', isLastResort: false }].","commonSituations":"Building a chain programmatically and forgetting to mark the final fallback model as isLastResort; overriding a chain via settings.json that omits the last-resort flag; copying a chain template and stripping the isLastResort field during a transform; all models set isLastResort to false explicitly.","solutions":["Mark exactly one entry as isLastResort: true — typically the most reliable, highest-quota model (e.g., the Flash model).","Use createSingleModelChain(model) for single-model setups, which automatically sets isLastResort: true.","Use the built-in chain builders (getDefaultPolicyChain, getFlashLitePolicyChain) which already include a correct last-resort entry.","When customizing a chain, always end with: chain[chain.length - 1].isLastResort = true."],"exampleFix":"// before — no last-resort entry\nconst chain = [\n  definePolicy({ model: 'gemini-pro' }),\n  definePolicy({ model: 'gemini-flash' }),\n];\nvalidateModelPolicyChain(chain); // throws\n\n// after — mark the fallback\nconst chain = [\n  definePolicy({ model: 'gemini-pro' }),\n  definePolicy({ model: 'gemini-flash', isLastResort: true }),\n];","handlingStrategy":"validation","validationCode":"// Ensure the chain has exactly one last-resort entry\nconst hasLastResort = chain.some((p) => p.isLastResort);\nif (!hasLastResort && chain.length > 0) {\n  chain[chain.length - 1].isLastResort = true;\n}\nvalidateModelPolicyChain(chain);","typeGuard":"function chainHasLastResort(chain: ModelPolicyChain): boolean {\n  return chain.some((p) => p.isLastResort === true);\n}","tryCatchPattern":"try {\n  validateModelPolicyChain(chain);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('isLastResort')) {\n    // Auto-fix: mark the last entry as last-resort\n    chain[chain.length - 1].isLastResort = true;\n    validateModelPolicyChain(chain);\n  } else throw e;\n}","preventionTips":["Always use built-in chain builders which include a correct last-resort entry.","After customizing a chain, verify the last-resort invariant.","Document that every chain must have exactly one isLastResort entry.","Add a unit test for chain validity after every transformation."],"tags":["model-policy","availability","fallback","validation"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}