Skip to content

Bug: Explicit thinkingLevel session override permanently reset to 'off' after each agent turn #87740

@TitanBob2026

Description

@TitanBob2026

Bug: Explicit thinkingLevel session override is permanently reset to off after each agent turn

OpenClaw version: 2026.5.27
Model: github-copilot/claude-sonnet-4.6
Channel: WebChat (Control UI) and custom Voice UI (both affected equally)


Description

When an explicit thinkingLevel override is set on a session via sessions.patch (e.g. "high"), the value is correctly stored and displayed in the UI. However, as soon as the agent completes a turn (i.e. the first reply bubble appears), the thinkingLevel in the session store is permanently overwritten with "off".

This does not happen when thinkingLevel is left unset (i.e. null / "inherited"). In that case, the resolved level is correctly inherited from the agent/provider default (in our case "high" for Claude Sonnet 4.6 via GitHub Copilot), and the behavior persists across turns without issue.


Steps to reproduce

  1. Open any session with model github-copilot/claude-sonnet-4.6
  2. Set thinkingLevel to "high" via sessions.patch (or via the thinking selector in the Control UI)
  3. Confirm the selector shows high
  4. Send any prompt
  5. As soon as the reply bubble appears, observe the thinking selector jump to off
  6. Check session state — thinkingLevel is now "off" in the store

Repeat: setting thinkingLevel back to "high" and sending another prompt resets it to "off" again every single time.

Using "inherited" (i.e. null / no override) works correctly and persists.


Root cause (traced in source)

In get-reply-DuA7xbHV.js, around the thinking-level resolution and fallback block:

// ~line 2837
const thinkingLevelSupported = isThinkingLevelSupported({
  provider,
  model,
  level: resolvedThinkLevel,
  catalog: thinkingCatalog
});

// ~line 2848 — if not supported, fallback is resolved
if (!thinkingLevelSupported) {
  const fallbackThinkLevel = resolveSupportedThinkingLevel({ ... });

  if (fallbackThinkLevel !== resolvedThinkLevel) {
    resolvedThinkLevel = fallbackThinkLevel;

    // ← THIS permanently mutates the session store:
    if (sessionEntry && sessionStore && sessionKey && sessionEntry.thinkingLevel === previousThinkLevel) {
      sessionEntry.thinkingLevel = fallbackThinkLevel;  // writes "off" back to store
      sessionEntry.updatedAt = Date.now();
      sessionStore[sessionKey] = sessionEntry;
      await updateSessionStore(storePath, (store) => {
        store[sessionKey] = sessionEntry;
      });
    }
  }
}

The fallback path is triggered because isThinkingLevelSupported returns false for level: "high" with github-copilot/claude-sonnet-4.6 at runtime — even though the provider plugin's resolveThinkingProfile clearly includes { id: "high" } in its levels array (confirmed in github-copilot/index.js).

The result is that the fallback resolves to "off" and is written back into the persistent session store, silently destroying the user's explicit override on every turn.


Expected behavior

  • sessions.patch { thinkingLevel: "high" } should persist across turns
  • If a thinking level is not supported at runtime for a given model, the session store should not be mutated — the fallback should only apply for that turn
  • Alternatively: the UI should be informed of the remapping so it can display the actual effective level

Additional context

  • "inherited" (no override / null) works correctly — the resolved level is "high" via provider default and is never mutated
  • The bug is reproducible in both the official Control UI (WebChat) and a custom Voice UI built on the Gateway Bridge
  • The GitHub Copilot provider plugin explicitly lists "high" as a supported level for claude-sonnet-4.6, so the runtime isThinkingLevelSupported check appears to be disagreeing with the plugin's own profile declaration
  • This has been present since at least v2026.4.22 and persists through v2026.5.27

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.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.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.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