{"record":{"id":"796844c9426ecddc","repo":"mvanhorn/last30days-skill","slug":"role-must-use-a-gemini-3-1-model-got-model","errorCode":null,"errorMessage":"{role} must use a Gemini 3.1 model. Got: {model}","messagePattern":"(.+?) must use a Gemini 3\\.1 model\\. Got: (.+?)","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/providers.py","lineNumber":330,"sourceCode":"        return runtime, OpenRouterClient(openrouter_key)\n\n    raise RuntimeError(f\"Unsupported reasoning provider: {provider_name}\")\n\n\ndef _resolve_x_backend(config: dict[str, Any]) -> str | None:\n    \"\"\"Resolve the X backend for runtime fetch.\n\n    Delegates to env.get_x_source which handles:\n    - Any known pin (X_BACKEND_KNOWN) exclusively: returns pin if available, None otherwise\n    - Unpinned: walks auto-chain (X_BACKEND_ORDER) only, never auto-selects opt-in backends\n    \"\"\"\n    return env.get_x_source(config)\n\n\ndef _require_gemini_31(model: str, *, role: str) -> None:\n    if model.startswith(\"gemini-3.1-\"):\n        return\n    raise RuntimeError(\n        f\"{role} must use a Gemini 3.1 model. Got: {model}\"\n    )\n\n\ndef extract_json(text: str) -> dict[str, Any]:\n    \"\"\"Extract the first JSON object from a model response.\"\"\"\n    text = text.strip()\n    if not text:\n        raise ValueError(\"Expected JSON response, got empty text\")\n    try:\n        return json.loads(text)\n    except json.JSONDecodeError:\n        match = re.search(r\"\\{[\\s\\S]*\\}\", text)\n        if not match:\n            raise\n        return json.loads(match.group(0))\n\n","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/providers.py#L312-L348","documentation":"RuntimeError from _require_gemini_31 enforcing that planner and rerank model pins start with 'gemini-3.1-' when the Gemini provider is selected. It is a hard pin: older gemini-1.5/2.x model overrides are rejected at runtime resolution time, not silently downgraded.","triggerScenarios":"Provider gemini with LAST30DAYS_PLANNER_MODEL / LAST30DAYS_RERANK_MODEL (or depth-based defaults) resolving to anything not prefixed 'gemini-3.1-', e.g. 'gemini-2.5-pro' or a typo'd pin. Both roles are checked before the runtime is constructed.","commonSituations":"Carry-over env overrides from before the 3.1 pin; docs/blog examples citing older model ids; users pinning flash/lite variants with wrong version strings; forks relaxing the pin in one call site but not this helper.","solutions":["Update model overrides to gemini-3.1-* variants (e.g. gemini-3.1-pro)","Unset custom model pins so the built-in defaults (already 3.1) apply","If you genuinely must run older models, change the pin policy in _require_gemini_31 consciously - do not bypass the check"],"exampleFix":"# before\nLAST30DAYS_PLANNER_MODEL=gemini-2.5-pro\n\n# after\nLAST30DAYS_PLANNER_MODEL=gemini-3.1-pro","handlingStrategy":"validation","validationCode":"for var in (\"LAST30DAYS_PLANNER_MODEL\", \"LAST30DAYS_RERANK_MODEL\"):\n    model = os.environ.get(var, \"\")\n    if model and not model.startswith(\"gemini-3.1-\"):\n        raise SystemExit(f\"{var}={model!r} must start with 'gemini-3.1-'\")","typeGuard":"def is_gemini_31(model: str) -> bool:\n    return model.startswith(\"gemini-3.1-\")","tryCatchPattern":"try:\n    runtime, client = resolve_runtime(config, depth)\nexcept RuntimeError as e:\n    if \"must use a Gemini 3.1 model\" in str(e):\n        config.pop(\"LAST30DAYS_PLANNER_MODEL\", None)\n        config.pop(\"LAST30DAYS_RERANK_MODEL\", None)\n        runtime, client = resolve_runtime(config, depth)  # defaults satisfy the pin","preventionTips":["Unset custom model overrides when upgrading to versions that pin 3.1","Copy model ids from the library's own defaults, not external docs","Grep .env for gemini-* pins after every version bump"],"tags":["providers","gemini","model-pin","versioning"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}