{"record":{"id":"acbb1c453ff303c8","repo":"nexu-io/open-design","slug":"role-must-use-a-gemini-3-1-preview-model-got","errorCode":null,"errorMessage":"{role} must use a Gemini 3.1 preview model. Got: {model}","messagePattern":"(.+?) must use a Gemini 3\\.1 preview model\\. Got: (.+?)","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"design-templates/last30days/scripts/lib/providers.py","lineNumber":350,"sourceCode":"            rerank_model=rerank_model,\n            x_search_backend=_resolve_x_backend(config),\n        )\n        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    preferred = (config.get(\"LAST30DAYS_X_BACKEND\") or \"\").lower()\n    if preferred in {\"xai\", \"bird\"}:\n        return preferred\n    return env.get_x_source(config)\n\n\ndef _require_gemini_31_preview(model: str, *, role: str) -> None:\n    if model.startswith(\"gemini-3.1-\") and model.endswith(\"-preview\"):\n        return\n    raise RuntimeError(\n        f\"{role} must use a Gemini 3.1 preview 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":332,"sourceCodeEnd":368,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/design-templates/last30days/scripts/lib/providers.py#L332-L368","documentation":"Raised by _require_gemini_31_preview, which is invoked from _resolve_model_pins ONLY when provider_name == 'gemini'. Both the planner and rerank model strings must start with 'gemini-3.1-' AND end with '-preview'. Any other model name (including gemini-2.x, gemini-3.0, or non-preview gemini-3.1) is rejected for the gemini provider.","triggerScenarios":"Setting LAST30DAYS_PLANNER_MODEL or LAST30DAYS_RERANK_MODEL to a non-preview model while LAST30DAYS_REASONING_PROVIDER=gemini. The role in the message identifies which pin failed ('planner' or 'rerank').","commonSituations":"Pinning an older model like 'gemini-2.0-flash' or 'gemini-pro' that predates the 3.1-preview contract. Copying a model name from another provider's docs. A fork that loosened the contract being reverted.","solutions":["Pin both LAST30DAYS_PLANNER_MODEL and LAST30DAYS_RERANK_MODEL to a name matching gemini-3.1-*-preview.","Leave both unset to inherit the defaults from _MODEL_DEFAULTS (which already satisfy the contract).","If you genuinely need a non-3.1-preview Gemini model, switch provider away from 'gemini' or relax _require_gemini_31_preview deliberately."],"exampleFix":"# before\nLAST30DAYS_REASONING_PROVIDER=gemini\nLAST30DAYS_PLANNER_MODEL=gemini-2.0-flash\n\n# after\nLAST30DAYS_REASONING_PROVIDER=gemini\n# unset both to use defaults, or:\nLAST30DAYS_PLANNER_MODEL=gemini-3.1-flash-preview","handlingStrategy":"validation","validationCode":"import re\n\ndef validate_gemini_model_pin(model, role):\n    if not (model.startswith(\"gemini-3.1-\") and model.endswith(\"-preview\")):\n        raise ValueError(\n            f\"{role} model must match gemini-3.1-*-preview; got {model!r}\"\n        )\n    return model","typeGuard":"null","tryCatchPattern":"null","preventionTips":["For the gemini provider, prefer leaving LAST30DAYS_PLANNER_MODEL / LAST30DAYS_RERANK_MODEL unset to inherit compliant defaults.","When pinning a custom model, validate it matches gemini-3.1-*-preview before launching a run.","Treat the contract change (3.1-preview required) as a versioned invariant; document it in the upgrade notes."],"tags":["config","model-version","gemini","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}