Skip to content

Fix: preserve modelOverride in agent handler (#5369)#15

Open
BingqingLyu wants to merge 2 commits into
mainfrom
fork-pr-19328-fix-5369-subagent-model-override-race
Open

Fix: preserve modelOverride in agent handler (#5369)#15
BingqingLyu wants to merge 2 commits into
mainfrom
fork-pr-19328-fix-5369-subagent-model-override-race

Conversation

@BingqingLyu

@BingqingLyu BingqingLyu commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the intermittent race condition where sub-agent model overrides were silently ignored (~3% failure rate).

  • When sessions_spawn sets a model override via sessions.patch, the agent handler could read stale cached data and overwrite the fresh modelOverride when writing back to the session store
  • The fix moves session entry construction inside the updateSessionStore mutator, ensuring it always uses fresh store data (loaded with skipCache: true)

Root Cause

The agent handler was:

  1. Reading session entry early via loadSessionEntry() (could return cached/stale data)
  2. Building nextEntry with modelOverride: entry?.modelOverride (potentially undefined)
  3. Calling updateSessionStore() which correctly re-reads with skipCache: true
  4. But the mutator ignored the fresh store data and just wrote store[key] = nextEntry

This meant freshly-written modelOverride values from sessions.patch could be overwritten.

Changes

  • src/gateway/server-methods/agent.ts: Build session entry inside updateSessionStore using fresh store data
  • src/gateway/server-methods/agent.test.ts: Add 4 comprehensive tests for the fix

Test plan

  • Existing tests updated and passing
  • New test: issue #5369: agent handler preserves fresh modelOverride from store
  • New test: issue #5369: request params override fresh store values for label/spawnedBy
  • New test: issue #5369: creates new entry when store has no existing entry
  • New test: issue #5369: preserves all important fields from fresh store
  • Full test suite passes (5,043 tests)

Fixes openclaw#5369

Supersedes openclaw#8398 (rebased onto latest main to resolve conflicts).

Greptile Summary

Fixes race condition where sub-agent modelOverride values set via sessions.patch could be silently overwritten. The agent handler previously built session entries using potentially stale cached data, then overwrote fresh store values. The fix moves session entry construction inside the updateSessionStore mutator, ensuring it always reads and uses fresh store data (loaded with skipCache: true).

  • Moved session entry construction from outside to inside updateSessionStore mutator (src/gateway/server-methods/agent.ts:391-443)
  • Uses freshEntry from store instead of stale entry for all fields that sessions.patch can modify
  • Adds proper sendPolicy denial handling inside the mutator with structured return value
  • Handles both storePath and no-storePath (fallback) cases correctly
  • Added 4 comprehensive regression tests specifically for issue sessions.patch model override not applied to spawned sub-agents openclaw/openclaw#5369
  • Updated existing tests to handle new mutator return value structure

Confidence Score: 5/5

  • Safe to merge - well-tested fix for documented race condition
  • The fix correctly addresses the root cause by ensuring session entry construction uses fresh store data. The implementation is clean, properly handles edge cases (storePath vs no-storePath, sendPolicy denial), and includes comprehensive test coverage specifically targeting the race condition. The changes are localized to the affected code path with minimal risk of side effects.
  • No files require special attention

Last reviewed commit: ffe4cde

(5/5) You can turn off certain types of comments like style here!

)

When building the session-entry patch in the agent handler, the cached
`entry` may be stale relative to the store — e.g. when sessions.patch
writes modelOverride between spawn and run (~3% failure rate for
sub-agent model overrides). mergeSessionEntry spreads
{...freshStore, ...patch}, so any field read from stale `entry` and
placed in the patch clobbers the fresh store value.

Fix by removing blind stale-writebacks from the patch. mergeSessionEntry
preserves anything not in the patch, so fresh modelOverride,
providerOverride, sendPolicy, skillsSnapshot, thinkingLevel,
cliSessionBindings, etc. survive the write.

Fixes openclaw#5369
Ensure no future change silently re-adds a stale-cache field read to the
agent session-entry patch. The original openclaw#5369 test only asserted
modelOverride and providerOverride; this one asserts the whole class
(sendPolicy, skillsSnapshot, thinkingLevel, fastMode, verboseLevel,
traceLevel, reasoningLevel, systemSent, spawnedWorkspaceDir, spawnDepth,
cliSessionIds, cliSessionBindings, claudeCliSessionId) survives a
stale-cache / fresh-store race. Verified to fail when any of those
fields is re-added to nextEntryPatch as `entry?.X`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sessions.patch model override not applied to spawned sub-agents

2 participants