openclaw - 💡(How to fix) Fix sessions_spawn(model=...) override silently dropped — written to runtime fields instead of override fields [1 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#74763Fetched 2026-05-01 05:41:36
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
2
Author
Timeline (top)
closed ×1commented ×1

Subagent runs spawned via sessions_spawn(model=...) (or the gateway subagent.spawn method's model param) silently ignore the requested model and run on the agent's resolved primary instead. The spawn response returns modelApplied: true, which is misleading.

Error Message

  • Silent failure — no error surfaced to caller.

Root Cause

In dist/subagent-spawn--QfZonyO.js, persistInitialChildSessionRuntimeModel writes the resolved override into the runtime/last-used fields:

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    model,
    ...provider ? { modelProvider: provider } : {}
});

But the agent runtime (dist/agent-command-BxeGYLZy.js, around line 510) reads explicit overrides from a different pair of fields:

const hasStoredOverride = Boolean(sessionEntry?.modelOverride || sessionEntry?.providerOverride);
// ...
const storedProviderOverride = sessionEntry?.providerOverride?.trim();
let storedModelOverride = sessionEntry?.modelOverride?.trim();

Mismatched field names → override silently dropped → run falls back to agent default.

buildDirectChildSessionPatch has the same problem (writes entry.model / entry.modelProvider from patch.model).

The gateway agent method DOES accept a provider/model request field, but subagent-spawn never passes them through; it relies entirely on the persisted entry to carry the override — and the persist writes to the wrong fields.

Fix Action

Fix

Change persistInitialChildSessionRuntimeModel (and analogous code in buildDirectChildSessionPatch) to write to the override fields:

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    modelOverride: model,
    modelOverrideSource: "user",
    ...provider ? { providerOverride: provider } : {}
});

Verified locally on 2026.4.26: spawning with sonnet-4.6 override now produces provider: github-copilot, modelId: claude-sonnet-4.6 in the trajectory, and the corresponding model_change entry is emitted.

Code Example

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    model,
    ...provider ? { modelProvider: provider } : {}
});

---

const hasStoredOverride = Boolean(sessionEntry?.modelOverride || sessionEntry?.providerOverride);
// ...
const storedProviderOverride = sessionEntry?.providerOverride?.trim();
let storedModelOverride = sessionEntry?.modelOverride?.trim();

---

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    modelOverride: model,
    modelOverrideSource: "user",
    ...provider ? { providerOverride: provider } : {}
});
RAW_BUFFERClick to expand / collapse

Summary

Subagent runs spawned via sessions_spawn(model=...) (or the gateway subagent.spawn method's model param) silently ignore the requested model and run on the agent's resolved primary instead. The spawn response returns modelApplied: true, which is misleading.

Root Cause

In dist/subagent-spawn--QfZonyO.js, persistInitialChildSessionRuntimeModel writes the resolved override into the runtime/last-used fields:

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    model,
    ...provider ? { modelProvider: provider } : {}
});

But the agent runtime (dist/agent-command-BxeGYLZy.js, around line 510) reads explicit overrides from a different pair of fields:

const hasStoredOverride = Boolean(sessionEntry?.modelOverride || sessionEntry?.providerOverride);
// ...
const storedProviderOverride = sessionEntry?.providerOverride?.trim();
let storedModelOverride = sessionEntry?.modelOverride?.trim();

Mismatched field names → override silently dropped → run falls back to agent default.

buildDirectChildSessionPatch has the same problem (writes entry.model / entry.modelProvider from patch.model).

The gateway agent method DOES accept a provider/model request field, but subagent-spawn never passes them through; it relies entirely on the persisted entry to carry the override — and the persist writes to the wrong fields.

Reproduction

  1. Spawn any agent with a model override that differs from its primary, e.g. sessions_spawn(agentId="research", model="github-copilot/claude-sonnet-4.6", task="Reply OK").
  2. Inspect the trajectory (~/.openclaw/agents/<agent>/sessions/<sessionId>.trajectory.jsonl) — provider/modelId show the agent default, not the override.
  3. Inspect ~/.openclaw/agents/<agent>/sessions/sessions.jsonmodelOverride/providerOverride are missing on the new entry; only model/modelProvider are set.

Fix

Change persistInitialChildSessionRuntimeModel (and analogous code in buildDirectChildSessionPatch) to write to the override fields:

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    modelOverride: model,
    modelOverrideSource: "user",
    ...provider ? { providerOverride: provider } : {}
});

Verified locally on 2026.4.26: spawning with sonnet-4.6 override now produces provider: github-copilot, modelId: claude-sonnet-4.6 in the trajectory, and the corresponding model_change entry is emitted.

Severity

  • Silent failure — no error surfaced to caller.
  • modelApplied: true in the spawn response is incorrect when the override is dropped.
  • Affects any caller relying on per-spawn model selection (e.g. model bake-offs, A/B comparisons, escalation patterns).

Affected Version

openclaw 2026.4.26 (latest as of this report). Likely present in earlier versions — the divergent runtime vs override field convention pre-dates the dist file's current shape.

extent analysis

TL;DR

Update persistInitialChildSessionRuntimeModel to write to the correct override fields (modelOverride and providerOverride) to ensure the requested model is applied.

Guidance

  • Identify the persistInitialChildSessionRuntimeModel function in dist/subagent-spawn--QfZonyO.js and update it to write to the correct fields.
  • Verify the fix by spawning an agent with a model override and checking the trajectory and session files for the correct provider and modelId.
  • Check the buildDirectChildSessionPatch function for similar issues and update it if necessary.
  • Test the fix with different model overrides to ensure it works as expected.

Example

store[target.canonicalKey] = mergeSessionEntry(store[target.canonicalKey], {
    modelOverride: model,
    modelOverrideSource: "user",
    ...provider ? { providerOverride: provider } : {}
});

Notes

This fix assumes that the issue is limited to the persistInitialChildSessionRuntimeModel function and the buildDirectChildSessionPatch function. Additional testing may be necessary to ensure that the fix does not introduce new issues.

Recommendation

Apply the workaround by updating the persistInitialChildSessionRuntimeModel function to write to the correct override fields, as this will ensure that the requested model is applied correctly.

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 sessions_spawn(model=...) override silently dropped — written to runtime fields instead of override fields [1 comments, 2 participants]