Skip to content

[Bug]: cron preflight normalizes LiteLLM-backed Gemini model allowlist entries to Google preview IDs #84745

@pigfoot

Description

@pigfoot

Triage note

This report is intentionally structured for automated/model triage: the reproduction is deterministic, the observed/expected behavior is explicit, and the suspected code path is called out separately from the observed evidence.

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

After upgrading from OpenClaw 2026.5.7 to 2026.5.18, cron agentTurn payloads using a configured non-Google provider model such as litellm/gemini-3-flash are rejected by cron preflight because the runtime allowlist is normalized to litellm/gemini-3-flash-preview.

Steps to reproduce

  1. Configure a custom/provider-backed model catalog entry under a non-Google LiteLLM-backed provider, for example models.providers.litellm.models[].id = "gemini-3-flash".
  2. Add the same model to agents.defaults.models as litellm/gemini-3-flash.
  3. Create a cron job with sessionTarget = "isolated" and payload.kind = "agentTurn", setting payload.model = "litellm/gemini-3-flash".
  4. Force-run the cron job.
  5. Observe cron preflight rejecting the payload model because the effective allowlist contains litellm/gemini-3-flash-preview instead of litellm/gemini-3-flash.

Expected behavior

A cron payload model should be accepted when it exactly matches a configured provider catalog entry and an agents.defaults.models allowlist entry for that same provider/model key.

In the observed setup, litellm/gemini-3-flash should remain litellm/gemini-3-flash because the configured provider is a custom LiteLLM-backed OpenAI-compatible provider. Google preview model-id normalization should not rewrite the model id unless the provider is Google/Google Gemini CLI/Google Vertex, or the configured provider explicitly opts into such normalization.

Actual behavior

On OpenClaw 2026.5.18, a delivery-none smoke cron using payload.model = "litellm/gemini-3-flash" failed at preflight. The diagnostic reported that litellm/gemini-3-flash was not in the allowlist, while the effective allowlist contained litellm/gemini-3-flash-preview and litellm/gemini-3.1-pro-preview.

This also affected real cron jobs that used litellm/gemini-3-flash as their payload model. Rolling back to OpenClaw 2026.5.7 made the same cron jobs run successfully without changing the cron jobs or the source config.

OpenClaw version

First known bad: 2026.5.12 / 2026.5.13 family; reproduced again on 2026.5.18.

Last known good: 2026.5.7.

Operating system

Linux 6.12.68+ x64, container/GKE-style runtime.

Install method

Containerized OpenClaw gateway runtime.

Model

litellm/gemini-3-flash as cron payload.model.

Provider / routing chain

OpenClaw cron isolated agent -> custom LiteLLM-backed provider -> OpenAI-compatible completions endpoint.

Additional provider/model setup details

Relevant source config uses non-preview model ids:

{
  "models": {
    "providers": {
      "litellm": {
        "api": "openai-completions",
        "models": [
          { "id": "gemini-3-flash", "name": "Gemini 3 Flash" },
          { "id": "gemini-3.1-pro", "name": "Gemini 3.1 Pro" }
        ]
      }
    }
  },
  "agents": {
    "defaults": {
      "models": {
        "litellm/gemini-3-flash": {},
        "litellm/gemini-3.1-pro": {}
      }
    }
  }
}

The issue is not that the provider cannot serve the model. The failure happens before dispatch, during cron model allowlist/preflight validation.

Logs, screenshots, and evidence

Observed timeline:

  • On 2026.5.12/2026.5.13, source config and cron payloads both used non-preview litellm/gemini-3-flash, but runtime cron preflight rejected payload.model = "litellm/gemini-3-flash" because the effective allowlist contained preview-normalized entries such as litellm/gemini-3-flash-preview and litellm/gemini-3.1-pro-preview.
  • Rolling back to 2026.5.7 resolved the same cron jobs without changing cron/openclaw.json.
  • On 2026.5.18, the regression reproduced again with a temporary delivery-none smoke cron using payload.model = "litellm/gemini-3-flash".

Representative diagnostic, redacted/abridged:

cron payload.model 'litellm/gemini-3-flash' rejected by agents.defaults.models allowlist:
litellm/gemini-3-flash is not in [..., litellm/gemini-3-flash-preview, litellm/gemini-3.1-pro-preview, ...]

Likely code path from current openclaw/openclaw source (cd019cfa when checked):

  • src/plugin-sdk/provider-model-id-normalize.ts defines Google preview normalization, including gemini-3-flash -> gemini-3-flash-preview and gemini-3.1-pro -> gemini-3.1-pro-preview.
  • src/agents/model-ref-shared.ts is provider-scoped for this normalization in normalizeBuiltInProviderModelId(), applying it only for google, google-gemini-cli, and google-vertex.
  • But src/config/model-input.ts currently has normalizeAgentModelRefForConfig(model) split provider/model and then call normalizeGooglePreviewModelId() on the suffix regardless of provider:
const provider = normalizeProviderId(trimmed.slice(0, slash));
const normalizedModel = normalizeGooglePreviewModelId(trimmed.slice(slash + 1));
return modelKeyForConfig(provider, normalizedModel);

This can rewrite litellm/gemini-3-flash into litellm/gemini-3-flash-preview while preserving the non-Google provider prefix, which matches the observed runtime allowlist mismatch.

Cron then rejects the original payload model in src/cron/isolated-agent/model-selection.ts via resolveAllowedModelRef() / allowlist validation, producing the observed cron payload.model ... rejected by agents.defaults.models allowlist error.

Impact and severity

Affected: cron jobs that use payload.model with custom/non-Google LiteLLM-backed provider ids whose model ids happen to match Google Gemini preview-normalization aliases, e.g. litellm/gemini-3-flash.

Severity: High for affected installations because scheduled jobs fail before model dispatch.

Frequency: Always for the affected cron preflight path on 2026.5.18 in the observed setup.

Consequence: scheduled jobs silently accumulate cron preflight failures until the user changes the payload model, updates config, rolls back, or the normalization bug is fixed.

Additional information

Temporary workaround: migrate affected cron payloads away from litellm/gemini-3-flash to a non-affected configured model such as litellm/minimax-m2.5-aws or another model id that does not trigger Gemini preview normalization.

Interactive agent primary model paths may not fail the same way: in the observed setup, an interactive agent configured with litellm/gemini-3-flash was runtime-normalized and could still execute, but the effective model differed from source config. The hard failure was specifically reproduced in cron payload preflight.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions