openclaw - 💡(How to fix) Fix Session model pin stored without `openrouter/` prefix — bare provider slugs persist across restarts and route to native plugins [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#70572Fetched 2026-04-24 05:56:18
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0

The Control UI model dropdown appears to post a bare provider slug (e.g. nvidia/nemotron-3-super-120b-a12b:free) to the gateway when a user picks a model listed under the OpenRouter catalog. The gateway stores this as-given in agents/main/sessions/sessions.json. On subsequent requests, the bare slug's nvidia/ prefix routes to the native NVIDIA-direct provider plugin (which lacks an API key in OpenRouter-only deployments), not the OpenRouter plugin.

Same bug class as #70354 (un-prefixed slugs routing to native provider plugins), but at the session-state layer — so it survives agents.defaults.model.primary swaps and is not fixed by editing openclaw.json. Users who only touch the main config will see the session continue to fail routing even after the root-cause-looking config is correct.

Error Message

  • If the bare slug (nvidia/...) doesn't match a registry key but openrouter/nvidia/... does, auto-prefix (or reject with a clear error message).
  • Same check could warn when a slug references a model whose upstream provider lists it as deprecated / scheduled-for-removal (elephant-alpha case).

Root Cause

A heartbeat cron on one of our nodes started returning an OAuth prompt from the google/gemma-4-31b-it:free fallback — the native Google provider plugin was attempting OAuth because the bare google/... slug routed there instead of to the OpenRouter plugin (which has our key). Pulling at that thread → discovered the session had a bare nvidia/...:free primary pin (our configured primary, but stripped of the openrouter/ prefix somewhere between UI click and persistence).

Fix Action

Fix / Workaround

Same bug class as #70354 (un-prefixed slugs routing to native provider plugins), but at the session-state layer — so it survives agents.defaults.model.primary swaps and is not fixed by editing openclaw.json. Users who only touch the main config will see the session continue to fail routing even after the root-cause-looking config is correct.

Our workaround

  • #70354 — update.run / SIGUSR1 supervisor restart. Same un-prefixed-slug bug family, different layer. Both got papered-over by the same deploy-side mitigation pattern in our kit.

Code Example

{
  "agent:main:main": {
    "systemPromptReport": { "model": "nvidia/nemotron-3-super-120b-a12b:free" }
  },
  "agent:main:main:heartbeat": {
    "systemPromptReport": { "model": "nvidia/nemotron-3-super-120b-a12b:free" },
    "model": "nvidia/nemotron-3-super-120b-a12b:free"
  }
}
RAW_BUFFERClick to expand / collapse

Summary

The Control UI model dropdown appears to post a bare provider slug (e.g. nvidia/nemotron-3-super-120b-a12b:free) to the gateway when a user picks a model listed under the OpenRouter catalog. The gateway stores this as-given in agents/main/sessions/sessions.json. On subsequent requests, the bare slug's nvidia/ prefix routes to the native NVIDIA-direct provider plugin (which lacks an API key in OpenRouter-only deployments), not the OpenRouter plugin.

Same bug class as #70354 (un-prefixed slugs routing to native provider plugins), but at the session-state layer — so it survives agents.defaults.model.primary swaps and is not fixed by editing openclaw.json. Users who only touch the main config will see the session continue to fail routing even after the root-cause-looking config is correct.

How we first noticed

A heartbeat cron on one of our nodes started returning an OAuth prompt from the google/gemma-4-31b-it:free fallback — the native Google provider plugin was attempting OAuth because the bare google/... slug routed there instead of to the OpenRouter plugin (which has our key). Pulling at that thread → discovered the session had a bare nvidia/...:free primary pin (our configured primary, but stripped of the openrouter/ prefix somewhere between UI click and persistence).

Then separately, on another node, the same session was pinned to openrouter/elephant-alpha. Elephant Alpha is scheduled for deprecation on 2026-04-29 upstream on OpenRouter (still live at time of filing, but retiring in ~6 days). So the gateway is still serving from elephant-alpha today, but that session will break the moment the slug 404s — and the user has no visible pointer from agents.defaults to that pin. This is how persisted session slugs silently outlive model lifecycles.

Reproduction

Typical OpenRouter-only deploy (no native provider keys configured):

  1. In openclaw.json, set agents.defaults.model.primary = "openrouter/nvidia/nemotron-3-super-120b-a12b:free". Restart gateway — primary resolves correctly, chat works.
  2. In the Control UI, open a chat session → model dropdown → pick NVIDIA: Nemotron 3 Super (free) from the OpenRouter catalog list.
  3. Inspect ~/.openclaw/agents/main/sessions/sessions.json: the "model" fields for that session now contain bare nvidia/nemotron-3-super-120b-a12b:free (no openrouter/ prefix).
  4. Chat requests for that session route to the NVIDIA-direct plugin and fail (No API key found for provider "nvidia" or routing 404).

The Gemma / Google variant reproduces the same way: pick Google: Gemma 4 31B (free) from the dropdown → google/gemma-4-31b-it:free persists bare → requests hit the native Google plugin → google plugin tries OAuth instead of OpenRouter key.

Observed state (sanitized, from one of our nodes)

{
  "agent:main:main": {
    "systemPromptReport": { "model": "nvidia/nemotron-3-super-120b-a12b:free" }
  },
  "agent:main:main:heartbeat": {
    "systemPromptReport": { "model": "nvidia/nemotron-3-super-120b-a12b:free" },
    "model": "nvidia/nemotron-3-super-120b-a12b:free"
  }
}

Plus historical openrouter/elephant-alpha pins on a second node. A single sessions.json rewrite pass converted them both to the current configured primary and gateway routing recovered. But no config-layer edit would have caught either one.

Suggested fixes — one would be enough

Control UI side: when emitting the model-pick message to the gateway, keep the fully-qualified slug (openrouter/<provider>/<model>) exactly as shown in the catalog. Don't strip the provider prefix in the POST payload.

Gateway side: on session.model write, validate against agents.defaults.models registry.

  • If the bare slug (nvidia/...) doesn't match a registry key but openrouter/nvidia/... does, auto-prefix (or reject with a clear error message).
  • Same check could warn when a slug references a model whose upstream provider lists it as deprecated / scheduled-for-removal (elephant-alpha case).

The gateway-side fix is more defensible because it catches ANY caller (UI, API, hand-edited config), not just the UI.

Our workaround

Reactive script that walks sessions.json, prefixes bare provider slugs with openrouter/, rewrites deprecated slugs (elephant-alpha, old ling-2.6-flash) to agents.defaults.model.primary, and restarts the gateway. Published in our fleet's deploy kit as fix-session-models.sh — kicks in after the wrong slug has already been pinned, so it's purely remedial.

Deploy context

  • Gateway: [email protected] (latest at time of filing) — verified affected. Prior 2026.4.20 also affected (nodes were pinned to broken slugs well before today's upgrade).
  • Transport: OpenRouter-only (OPENROUTER_API_KEY in env; no native provider keys).
  • Affected agents: both main chat (agent:main:main) and heartbeat cron (agent:main:main:heartbeat).
  • Replicated across 3 independent deploys in our fleet (2× OCI aarch64 systemd, 1× macOS Docker).

Related

  • #70354 — update.run / SIGUSR1 supervisor restart. Same un-prefixed-slug bug family, different layer. Both got papered-over by the same deploy-side mitigation pattern in our kit.

extent analysis

TL;DR

The most likely fix is to modify the Control UI to include the fully-qualified slug (openrouter/<provider>/<model>) when emitting the model-pick message to the gateway, or implement a validation check on the gateway side to auto-prefix or reject bare slugs.

Guidance

  • Modify the Control UI to keep the fully-qualified slug when sending the model selection to the gateway.
  • Implement a validation check on the gateway side to ensure that the model slug is properly prefixed with openrouter/ before storing it in the session state.
  • Consider adding a warning or error message when a deprecated model slug is used to prevent future issues.
  • Review the sessions.json file for any existing bare or deprecated slugs and update them to the fully-qualified format.

Example

No code example is provided as the issue does not include specific code snippets that need to be modified. However, the suggested fixes provide a clear direction for the necessary changes.

Notes

The provided workaround script fix-session-models.sh can be used as a temporary solution to remediate existing issues, but a permanent fix should be implemented to prevent future occurrences.

Recommendation

Apply the gateway-side fix to validate and auto-prefix model slugs, as it provides a more comprehensive solution that catches any caller, not just the UI. This approach ensures that the issue is addressed at the source and prevents similar problems in the future.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Session model pin stored without `openrouter/` prefix — bare provider slugs persist across restarts and route to native plugins [1 participants]