openclaw - ✅(Solved) Fix [Bug]: Auto-onboard / plugin presets unconditionally overwrite user-set agents.defaults.model.primary [1 pull requests, 2 comments, 2 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#75720Fetched 2026-05-02 05:31:11
View on GitHub
Comments
2
Participants
2
Timeline
3
Reactions
2
Author
Timeline (top)
commented ×2cross-referenced ×1

When OpenClaw auto-updates and the new release ships new provider plugins, the auto-onboarder runs each plugin's preset applier on first load. Each preset applier unconditionally writes its own primaryModelRef into agents.defaults.model.primary, overwriting an existing user-set primary with no check. Custom (non-stock) provider models in agents.defaults.models get stripped as orphans during the rebuild. The user discovers the swap days later via downstream symptoms (rate-limit failures on the auto-promoted model, or fallback chain failures).

Root Cause

User's claude-max-proxy/* model registrations were stripped from agents.defaults.models (treated as orphans because no plugin owns them). primary and fallbacks overwritten with the new plugins' preferred defaults. Symptoms surfaced ~6 hours later when ChatGPT Plus quota exhausted and the moonshot fallback hit No API key found (because moonshot's auth: [] self-registered with no actual auth).

Fix Action

Fix / Workaround

Workarounds users have to apply

PR fix notes

PR #75748: fix: preserve provider default model selection

Description (problem / solution / changelog)

Summary

  • Problem: provider presets/auth config patches could overwrite an existing agents.defaults.model.primary and fallbacks while registering new provider defaults.
  • Why it matters: auto-onboarded or newly available provider plugins could silently move users off custom model selections, surfacing later as quota/auth failures.
  • What changed: provider preset helpers and auth patch application now preserve existing default model selection unless an explicit default replacement path is used.
  • What did NOT change (scope boundary): explicit openclaw models set and openclaw models auth login --set-default still replace the default model.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #75720
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: provider-owned preset helpers and auth config patch merging treated provider default model choices as authoritative writes, so agents.defaults.model.primary could be replaced during setup/onboarding even when the user already had an explicit default.
  • Missing detection / guardrail: helper-level tests covered allowlist merging and explicit default setting, but not provider preset/auth patches against an existing custom primary/fallback pair.
  • Contributing context (if known): OpenAI and Moonshot setup paths could register new defaults while a custom provider-backed primary was already configured.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/commands/onboard-auth.config-shared.test.ts, src/plugins/provider-auth-choice-helpers.test.ts, src/commands/models/auth.test.ts, src/commands/auth-choice.apply.plugin-provider.test.ts, extensions/moonshot/index.test.ts, extensions/openai/default-models.test.ts
  • Scenario the test should lock in: provider setup may add model allowlist/provider entries but must keep existing default primary/fallbacks unless --set-default is used.
  • Why this is the smallest reliable guardrail: the bug is in shared config helper seams, so helper and command-path tests catch the regression without needing live provider credentials.
  • Existing test that already covers this (if any): existing --set-default tests continue to cover intentional replacement.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Provider auth/setup can make newly installed provider models available without replacing a user's existing default model. Users can still opt into replacement with openclaw models auth login --set-default or openclaw models set.

Diagram (if applicable)

Before:
[provider setup] -> [register model defaults] -> [replace user primary]

After:
[provider setup] -> [register model defaults] -> [keep user primary/fallbacks]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS local checkout
  • Runtime/container: Node/pnpm repo scripts
  • Model/provider: OpenAI/Moonshot/provider-auth helper paths
  • Integration/channel (if any): N/A
  • Relevant config (redacted): existing agents.defaults.model.primary plus provider setup patch adding OpenAI/Moonshot defaults

Steps

  1. Start with agents.defaults.model.primary set to a custom provider model and fallbacks set to a custom fallback.
  2. Apply provider preset/auth config that registers OpenAI/Moonshot defaults.
  3. Verify provider model entries are added while default primary/fallbacks remain unchanged.

Expected

  • Provider models are available additively.
  • Existing agents.defaults.model.primary and non-empty fallbacks are preserved.
  • --set-default still replaces the default model intentionally.

Actual

  • Fixed in this PR.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: targeted helper/command/provider tests listed below passed locally.
  • Edge cases checked: existing custom primary/fallbacks preserved; empty config still receives provider default; models auth login --set-default remains replacement path.
  • What you did not verify: full pnpm check:changed was not run because the requester asked to skip Testbox and proceed directly to PR.

Validation run locally:

pnpm test src/commands/onboard-auth.config-shared.test.ts src/plugins/provider-auth-choice-helpers.test.ts src/commands/models/auth.test.ts src/commands/auth-choice.apply.plugin-provider.test.ts extensions/moonshot/index.test.ts extensions/openai/default-models.test.ts
pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/cli/models.md docs/plugins/sdk-provider-plugins.md extensions/moonshot/index.test.ts extensions/openai/default-models.test.ts extensions/openai/default-models.ts src/commands/auth-choice.apply.plugin-provider.test.ts src/commands/models/auth.test.ts src/commands/models/auth.ts src/commands/onboard-auth.config-shared.test.ts src/plugin-sdk/provider-onboard.ts src/plugins/provider-api-key-auth.ts src/plugins/provider-auth-choice-helpers.test.ts src/plugins/provider-auth-choice-helpers.ts src/plugins/provider-auth-choice.ts src/plugins/provider-model-primary.ts src/plugins/types.ts
git diff --check

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: provider migrations that intentionally replace defaults could accidentally be preserved.
    • Mitigation: direct migration helper behavior remains opt-in; command paths still allow explicit replacement through --set-default, and existing migration replacement tests remain covered.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/cli/models.md (modified, +3/-0)
  • docs/plugins/sdk-provider-plugins.md (modified, +6/-0)
  • extensions/moonshot/index.test.ts (modified, +26/-0)
  • extensions/openai/default-models.test.ts (modified, +6/-2)
  • extensions/openai/default-models.ts (modified, +3/-1)
  • src/commands/auth-choice.apply.plugin-provider.test.ts (modified, +60/-0)
  • src/commands/models/auth.test.ts (modified, +93/-6)
  • src/commands/models/auth.ts (modified, +1/-0)
  • src/commands/onboard-auth.config-shared.test.ts (modified, +33/-0)
  • src/plugin-sdk/provider-onboard.ts (modified, +17/-4)
  • src/plugins/provider-api-key-auth.ts (modified, +16/-1)
  • src/plugins/provider-auth-choice-helpers.test.ts (modified, +108/-0)
  • src/plugins/provider-auth-choice-helpers.ts (modified, +78/-6)
  • src/plugins/provider-auth-choice.ts (modified, +6/-0)
  • src/plugins/provider-model-primary.ts (modified, +7/-2)
  • src/plugins/types.ts (modified, +2/-1)

Code Example

"agents": {
  "defaults": {
    "models": {
      "claude-max-proxy/claude-opus-4-7": {},
      "claude-max-proxy/claude-sonnet-4-6": {},
      "claude-max-proxy/claude-haiku-4-5": {}
    },
    "model": {
      "primary": "claude-max-proxy/claude-opus-4-7",
      "fallbacks": ["claude-max-proxy/claude-sonnet-4-6"]
    }
  }
}

---

"agents": {
  "defaults": {
    "models": {
      "openai-codex/gpt-5.4": {},
      "moonshot/kimi-k2.6": {}
    },
    "model": {
      "primary": "openai-codex/gpt-5.4",
      "fallbacks": ["moonshot/kimi-k2.6"]
    }
  }
}

---

const moonshotPresetAppliers = createDefaultModelPresetAppliers({
  primaryModelRef: MOONSHOT_DEFAULT_MODEL_REF,
  // ...
});

---

function applyProviderConfigWithDefaultModelPreset(cfg, params) {
  const next = applyProviderConfigWithDefaultModel(cfg, ...);
  return params.primaryModelRef
    ? applyAgentDefaultModelPrimary(next, params.primaryModelRef)
    : next;
}

---

const moonshotProviderDiscovery = {
  id: "moonshot",
  auth: [],                                   // ← claims no auth required
  staticCatalog: { run: async () => ({ provider: buildMoonshotProvider() }) }
};
RAW_BUFFERClick to expand / collapse

Summary

When OpenClaw auto-updates and the new release ships new provider plugins, the auto-onboarder runs each plugin's preset applier on first load. Each preset applier unconditionally writes its own primaryModelRef into agents.defaults.model.primary, overwriting an existing user-set primary with no check. Custom (non-stock) provider models in agents.defaults.models get stripped as orphans during the rebuild. The user discovers the swap days later via downstream symptoms (rate-limit failures on the auto-promoted model, or fallback chain failures).

Concrete reproduction (occurred in production 2026-04-27)

Pre-update state (v2026.4.24):

"agents": {
  "defaults": {
    "models": {
      "claude-max-proxy/claude-opus-4-7": {},
      "claude-max-proxy/claude-sonnet-4-6": {},
      "claude-max-proxy/claude-haiku-4-5": {}
    },
    "model": {
      "primary": "claude-max-proxy/claude-opus-4-7",
      "fallbacks": ["claude-max-proxy/claude-sonnet-4-6"]
    }
  }
}

(claude-max-proxy is a user-installed openai-completions proxy fronting Claude.ai sessions, not a stock OpenClaw plugin.)

Auto-update to v2026.4.25 brought new plugins: openai, openai-codex, moonshot. Auto-onboarder ran their presets. Result post-update (no user input):

"agents": {
  "defaults": {
    "models": {
      "openai-codex/gpt-5.4": {},
      "moonshot/kimi-k2.6": {}
    },
    "model": {
      "primary": "openai-codex/gpt-5.4",
      "fallbacks": ["moonshot/kimi-k2.6"]
    }
  }
}

User's claude-max-proxy/* model registrations were stripped from agents.defaults.models (treated as orphans because no plugin owns them). primary and fallbacks overwritten with the new plugins' preferred defaults. Symptoms surfaced ~6 hours later when ChatGPT Plus quota exhausted and the moonshot fallback hit No API key found (because moonshot's auth: [] self-registered with no actual auth).

Root cause in code

extensions/moonshot/onboard.ts:

const moonshotPresetAppliers = createDefaultModelPresetAppliers({
  primaryModelRef: MOONSHOT_DEFAULT_MODEL_REF,
  // ...
});

provider-onboard-*.jsapplyProviderConfigWithDefaultModelPreset():

function applyProviderConfigWithDefaultModelPreset(cfg, params) {
  const next = applyProviderConfigWithDefaultModel(cfg, ...);
  return params.primaryModelRef
    ? applyAgentDefaultModelPrimary(next, params.primaryModelRef)
    : next;
}

The applyAgentDefaultModelPrimary call unconditionally overwrites agents.defaults.model.primary with the plugin's claimed primary. No check against existing user value.

Compounded by extensions/moonshot/provider-discovery.js:

const moonshotProviderDiscovery = {
  id: "moonshot",
  auth: [],                                   // ← claims no auth required
  staticCatalog: { run: async () => ({ provider: buildMoonshotProvider() }) }
};

auth: [] means moonshot self-registers on plugin load regardless of whether the user has any moonshot credential. So the auto-onboarder's provider-model-configured candidate fires, and moonshot ends up promoted to fallback (or primary, depending on apply order).

Expected behavior

Plugin preset appliers should only set primary / fallbacks when those keys are absent or empty. If the user has explicitly set a primary, the plugin should:

  • register its own models in agents.defaults.models (additive),
  • but not touch primary / fallbacks.

Or: a meta.userOverrides block listing keys the user has explicitly set, which presets / doctor / auto-onboard all read and skip.

Workarounds users have to apply

  1. plugins.deny: ["openai", "moonshot"] — blocks specific plugins. Per-plugin, requires anticipating future plugin names. Adds maintenance burden every release.
  2. External cron polling agents.defaults.model.primary and alerting on drift — detection only, not prevention.

Severity

Medium-to-high operational impact. Silent overrides surface as delayed symptoms (rate limits, fallback failures, missing model auth) hours-to-days after the actual config write, making root cause hard to find. In our case it took ~3 hours of forensics across journal, plugin source, and config diffs to identify the auto-onboarder as the actor.

Related

  • #74910 — openclaw doctor strips deprecated keys without preserving the user value. Same family of issue (silent override of explicit user config) via a different code path.

Environment

  • v2026.4.29 (originally hit on v2026.4.25)
  • Linux x64
  • Custom claude-max-proxy provider (openai-completions wrapper around claude CLI)

extent analysis

TL;DR

Modify the applyProviderConfigWithDefaultModelPreset function to conditionally update agents.defaults.model.primary only when the key is absent or empty.

Guidance

  • Review the applyProviderConfigWithDefaultModelPreset function to ensure it checks for existing user-set values before overwriting agents.defaults.model.primary.
  • Consider introducing a meta.userOverrides block to track user-explicitly set keys and prevent silent overrides.
  • Evaluate the extensions/moonshot/provider-discovery.js file to ensure that the auth property is correctly configured to prevent self-registration without user credentials.
  • Investigate implementing a mechanism to preserve custom provider models in agents.defaults.models during the rebuild process.

Example

function applyProviderConfigWithDefaultModelPreset(cfg, params) {
  const next = applyProviderConfigWithDefaultModel(cfg, ...);
  if (!next.defaults.model.primary) { // Check if primary is absent or empty
    return params.primaryModelRef
      ? applyAgentDefaultModelPrimary(next, params.primaryModelRef)
      : next;
  }
  return next; // Preserve existing user-set primary
}

Notes

The proposed solution focuses on preventing silent overrides of user-explicitly set configuration values. However, a comprehensive fix may require additional changes to handle custom provider models and prevent their removal during the rebuild process.

Recommendation

Apply a workaround by modifying the applyProviderConfigWithDefaultModelPreset function to conditionally update agents.defaults.model.primary, as this will prevent silent overrides of user-set values. A more robust solution would involve introducing a meta.userOverrides block and preserving custom provider models, but this may require significant changes to the existing codebase.

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…

FAQ

Expected behavior

Plugin preset appliers should only set primary / fallbacks when those keys are absent or empty. If the user has explicitly set a primary, the plugin should:

  • register its own models in agents.defaults.models (additive),
  • but not touch primary / fallbacks.

Or: a meta.userOverrides block listing keys the user has explicitly set, which presets / doctor / auto-onboard all read and skip.

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 - ✅(Solved) Fix [Bug]: Auto-onboard / plugin presets unconditionally overwrite user-set agents.defaults.model.primary [1 pull requests, 2 comments, 2 participants]