mvanhorn/last30days-skill · error · RuntimeError
{role} must use a Gemini 3.1 model. Got: {model}
Error message
{role} must use a Gemini 3.1 model. Got: {model} What it means
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.
Source
Thrown at skills/last30days/scripts/lib/providers.py:330
return runtime, OpenRouterClient(openrouter_key)
raise RuntimeError(f"Unsupported reasoning provider: {provider_name}")
def _resolve_x_backend(config: dict[str, Any]) -> str | None:
"""Resolve the X backend for runtime fetch.
Delegates to env.get_x_source which handles:
- Any known pin (X_BACKEND_KNOWN) exclusively: returns pin if available, None otherwise
- Unpinned: walks auto-chain (X_BACKEND_ORDER) only, never auto-selects opt-in backends
"""
return env.get_x_source(config)
def _require_gemini_31(model: str, *, role: str) -> None:
if model.startswith("gemini-3.1-"):
return
raise RuntimeError(
f"{role} must use a Gemini 3.1 model. Got: {model}"
)
def extract_json(text: str) -> dict[str, Any]:
"""Extract the first JSON object from a model response."""
text = text.strip()
if not text:
raise ValueError("Expected JSON response, got empty text")
try:
return json.loads(text)
except json.JSONDecodeError:
match = re.search(r"\{[\s\S]*\}", text)
if not match:
raise
return json.loads(match.group(0))
View on GitHub (pinned to c7460f6114)
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
Example fix
# before LAST30DAYS_PLANNER_MODEL=gemini-2.5-pro # after LAST30DAYS_PLANNER_MODEL=gemini-3.1-pro
Defensive patterns
Strategy: validation
Validate before calling
for var in ("LAST30DAYS_PLANNER_MODEL", "LAST30DAYS_RERANK_MODEL"):
model = os.environ.get(var, "")
if model and not model.startswith("gemini-3.1-"):
raise SystemExit(f"{var}={model!r} must start with 'gemini-3.1-'") Type guard
def is_gemini_31(model: str) -> bool:
return model.startswith("gemini-3.1-") Try / catch
try:
runtime, client = resolve_runtime(config, depth)
except RuntimeError as e:
if "must use a Gemini 3.1 model" in str(e):
config.pop("LAST30DAYS_PLANNER_MODEL", None)
config.pop("LAST30DAYS_RERANK_MODEL", None)
runtime, client = resolve_runtime(config, depth) # defaults satisfy the pin Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Gemini selected but no Google API key is configured.
- Gemini response did not contain text.
- Gemini HTTP {exc.code}: {detail}
- Gemini request failed: {exc}
- Unsupported reasoning provider: {provider_name}
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/796844c9426ecddc.
Report an issue: GitHub.