mvanhorn/last30days-skill · critical · RuntimeError

OpenRouter selected but OPENROUTER_API_KEY is not configured

Error message

OpenRouter selected but OPENROUTER_API_KEY is not configured.

What it means

RuntimeError raised in resolve_runtime when provider is 'openrouter' but OPENROUTER_API_KEY is absent from config. Unlike the big-three providers the key is read inline from the config dict at this branch, so it must be present in whatever env/.env layer populates config.

Source

Thrown at skills/last30days/scripts/lib/providers.py:305

            openai_token
        )

    if provider_name == "xai":
        if not xai_key:
            raise RuntimeError("xAI selected but XAI_API_KEY is not configured.")
        runtime = schema.ProviderRuntime(
            reasoning_provider="xai",
            planner_model=planner_model,
            rerank_model=rerank_model,
    
            x_search_backend=_resolve_x_backend(config),
        )
        return runtime, XAIClient(xai_key)

    if provider_name == "openrouter":
        openrouter_key = config.get("OPENROUTER_API_KEY")
        if not openrouter_key:
            raise RuntimeError("OpenRouter selected but OPENROUTER_API_KEY is not configured.")
        runtime = schema.ProviderRuntime(
            reasoning_provider="openrouter",
            planner_model=planner_model,
            rerank_model=rerank_model,
            x_search_backend=_resolve_x_backend(config),
        )
        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
    """

View on GitHub (pinned to c7460f6114)

Solutions

  1. Add OPENROUTER_API_KEY to the loaded env layer (.env file or process env)
  2. Verify the harness/gateway invoking the engine passes the variable through
  3. Test with a trivial config load before the full run to confirm the key resolves

Example fix

# before
LAST30DAYS_REASONING_PROVIDER=openrouter
# no OPENROUTER_API_KEY

# after
LAST30DAYS_REASONING_PROVIDER=openrouter
OPENROUTER_API_KEY=sk-or-v1-...
Defensive patterns

Strategy: validation

Validate before calling

if (os.environ.get("LAST30DAYS_REASONING_PROVIDER") or "").lower() == "openrouter":
    if not os.environ.get("OPENROUTER_API_KEY"):
        raise SystemExit("Set OPENROUTER_API_KEY before selecting the openrouter provider")

Type guard

def openrouter_ready(config: dict) -> bool:
    provider = (config.get("LAST30DAYS_REASONING_PROVIDER") or "gemini").lower()
    return provider != "openrouter" or bool(config.get("OPENROUTER_API_KEY"))

Try / catch

try:
    runtime, client = resolve_runtime(config, depth)
except RuntimeError as e:
    if "OPENROUTER_API_KEY" in str(e):
        set_key_via_setup("OPENROUTER_API_KEY")

Prevention

When it happens

Trigger: LAST30DAYS_REASONING_PROVIDER=openrouter with OPENROUTER_API_KEY not set in the environment or ~/.config/last30days/.env; or set in the shell but the engine was launched from another context (cron, harness gateway) without it.

Common situations: OpenRouter key obtained from openrouter.ai/keys but never added to config; per-harness env inheritance gaps (gateway processes stripping env); typo'd variable name.

Related errors


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/dbf6f6cad969c98b. Report an issue: GitHub.