openclaw - ✅(Solved) Fix Session pin to fallback-only model silently removes fallback chain via dedup (fallbackConfigured:false) [1 pull requests, 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#77766Fetched 2026-05-06 06:21:50
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
2
Timeline (top)
referenced ×3commented ×1cross-referenced ×1mentioned ×1

When a session is pinned to a model that only exists in agents.defaults.model.fallbacks (not as primary), the fallback chain is silently reduced to length 1 via deduplication. The result is fallbackConfigured:false in the decision log — the session has no safety net despite a fallback chain being configured globally.

Error Message

Expected: fallback chain is consulted; at minimum the user gets an error from exhausted fallbacks with options When a session pin's effective primary matches a model in the configured fallback list, expand the candidate list with the next-tier fallback (e.g. if the pin is openai/gpt-5.5 and the global fallbacks are [openai/gpt-5.5], include the global primary venice/claude-sonnet-4-6 as a fallback candidate, or at minimum warn that the fallback chain is collapsed).

Root Cause

Root cause (traced in source)

Fix Action

Fix / Workaround

A simpler mitigation: when candidates.length === 1 and isPrimary && effectivePrimary !== configuredPrimary, add the configured primary as a fallback candidate.

PR fix notes

PR #77790: fix(agents): include configured primary as fallback when session pin collapses chain

Description (problem / solution / changelog)

Summary

Fixes #77766

When a session is pinned to a model that exists in the configured fallbacks list, resolveEffectiveModelFallbacks returns an empty array ([]). In resolveFallbackCandidates, the configured primary was only added as a last-resort fallback when fallbacksOverride was undefined — but not when it was an explicitly empty array. This collapsed the candidate list to a single entry, producing fallbackConfigured:false and leaving the session with no safety net.

Root Cause

resolveFallbackCandidates line 596:

if (params.fallbacksOverride === undefined && primary?.provider && primary.model) {
    addExplicitCandidate({ provider: primary.provider, model: primary.model });
}

When fallbacksOverride is [] (from user pin via resolveEffectiveModelFallbacks):

  • The fallback loop iterates zero times (empty array)
  • The guard fallbacksOverride === undefined is false ([] is defined)
  • The configured primary is never added
  • candidates.length === 1, hasFallbackCandidates = false

Fix

Widen the guard to also fire when fallbacksOverride is an empty array:

if (
  (params.fallbacksOverride === undefined || params.fallbacksOverride.length === 0) &&
  primary?.provider &&
  primary.model
) {
  addExplicitCandidate({ provider: primary.provider, model: primary.model });
}

This ensures the configured primary remains available as a last-resort fallback when session pin collapses the chain, while still respecting explicit non-empty fallbacksOverride lists (the caller's intent).

Real behavior proof

  • Behavior or issue addressed: Session pinned to a model in the fallbacks list loses all fallback candidates, resulting in fallbackConfigured:false and no recovery path when the pinned model fails.
  • Real environment tested: OpenClaw dev build on Linux x64 (Node v22.22.1), local fork at /mnt/data/repos/forks/openclaw, branch fix/fallback-chain-collapse-on-session-pin.
  • Exact steps or command run after fix:
    1. Checked out branch and ran the targeted test to verify the fix:
    2. npx vitest run src/agents/model-fallback.test.ts -t "includes configured primary as fallback when session pin collapses"
  • Evidence after fix:

Terminal output from npx vitest run on the fix branch:

 ✓ agents-core ../../src/agents/model-fallback.test.ts (68 tests | 67 skipped) 1295ms
       ✓ includes configured primary as fallback when session pin collapses fallback chain (#77766) 1293ms
 ✓ agents-support ../../src/agents/model-fallback.test.ts (68 tests | 67 skipped) 1297ms
       ✓ includes configured primary as fallback when session pin collapses fallback chain (#77766) 1295ms

 Test Files  2 passed (2)
      Tests  2 passed | 134 skipped (136)
   Start at  20:24:17
   Duration  4.13s (transform 544ms, setup 643ms, import 596ms, tests 2.59s, environment 0ms)

The test creates a config with primary venice/claude-sonnet-4-6 and fallback openai/gpt-5.5, pins the session to the fallback model with fallbacksOverride: [], makes the first call fail, and verifies the configured primary is tried as last resort. Before the fix, resolveFallbackCandidates would return only the pinned model; after the fix, it includes the configured primary as a fallback candidate.

  • Observed result after fix: The pinned session correctly falls back to the configured primary (venice/claude-sonnet-4-6) when the pinned model (openai/gpt-5.5) fails. runWithModelFallback makes 2 calls instead of 1, and the second call uses the configured primary. The guard params.fallbacksOverride === undefined || params.fallbacksOverride.length === 0 fires for both undefined and [].
  • What was not tested: Multi-provider failover across different API endpoints (tested single-provider with different models). Did not test with non-empty fallbacksOverride (existing tests cover that path).

Testing

  • Added test: includes configured primary as fallback when session pin collapses fallback chain (#77766) — verifies that fallbacksOverride: [] with a pinned model still falls back to the configured primary
  • Updated test: includes configured primary as last resort when fallbacksOverride is empty (#77766) — updated to match new behavior
  • Updated test: does not use fallback list for user session model overrides but keeps configured primary (#77766) — e2e agentCommand test updated to expect configured primary as fallback on user pin
  • All existing tests pass (2 pre-existing failures unrelated to this change: LiveSessionModelSwitchError and keeps configured fallback chain)

Changed files

  • src/agents/model-fallback.test.ts (modified, +115/-8)
  • src/agents/model-fallback.ts (modified, +12/-1)
  • src/commands/agent.test.ts (modified, +11/-4)

Code Example

const hasFallbackCandidates = candidates.length > 1;

---

model_fallback_decision: decision=candidate_failed
requestedProvider=openai, requestedModel=gpt-5.5
attempt=1, total=1
fallbackConfigured=false
fallbackStepFinalOutcome=chain_exhausted
RAW_BUFFERClick to expand / collapse

Summary

When a session is pinned to a model that only exists in agents.defaults.model.fallbacks (not as primary), the fallback chain is silently reduced to length 1 via deduplication. The result is fallbackConfigured:false in the decision log — the session has no safety net despite a fallback chain being configured globally.

Steps to reproduce

  1. Configure: agents.defaults.model.primary: venice/claude-sonnet-4-6, fallbacks: [openai/gpt-5.5]
  2. Pin the main session to openai/gpt-5.5 (e.g. via model switch)
  3. Trigger a timeout on openai/gpt-5.5

Expected: fallback chain is consulted; at minimum the user gets an error from exhausted fallbacks with options Actual: fallbackConfigured:false, chain_exhausted after 1 attempt, silent failure

Root cause (traced in source)

model-fallback-Bpl6-0ev.js line 662:

const hasFallbackCandidates = candidates.length > 1;

When effectivePrimary is openai/gpt-5.5 and the configured fallbacks list is [openai/gpt-5.5], the createModelCandidateCollector dedup set (seen) eliminates gpt-5.5 as a fallback candidate because it was already added as the primary. candidates.length === 1hasFallbackCandidates = false.

The fallback inclusion logic (lines 530–545) correctly detects that the effective primary differs from the configured primary and checks whether the effective primary exists in the fallback list — but this check serves to include fallbacks, not to expand them. The dedup step then collapses the list back to 1.

User-facing surprise

Pinning reduces resilience, which is the opposite of user expectation. A user who pins to a fallback model for testing assumes the global fallback chain remains operative.

Proposed fix

When a session pin's effective primary matches a model in the configured fallback list, expand the candidate list with the next-tier fallback (e.g. if the pin is openai/gpt-5.5 and the global fallbacks are [openai/gpt-5.5], include the global primary venice/claude-sonnet-4-6 as a fallback candidate, or at minimum warn that the fallback chain is collapsed).

A simpler mitigation: when candidates.length === 1 and isPrimary && effectivePrimary !== configuredPrimary, add the configured primary as a fallback candidate.

Evidence from logs

model_fallback_decision: decision=candidate_failed
requestedProvider=openai, requestedModel=gpt-5.5
attempt=1, total=1
fallbackConfigured=false
fallbackStepFinalOutcome=chain_exhausted

Version

OpenClaw 2026.5.3-1 (2eae30e), macOS 26.4.1 arm64

extent analysis

TL;DR

The most likely fix is to modify the fallback candidate collection logic to include the global primary model as a fallback candidate when a session is pinned to a model that exists in the fallback list.

Guidance

  • Review the createModelCandidateCollector function in model-fallback-Bpl6-0ev.js to understand how the deduplication step affects the fallback candidate list.
  • Consider implementing the proposed fix to expand the candidate list with the next-tier fallback when a session pin's effective primary matches a model in the configured fallback list.
  • As a simpler mitigation, add the configured primary as a fallback candidate when candidates.length === 1 and isPrimary && effectivePrimary !== configuredPrimary.
  • Verify the fix by checking the decision log for fallbackConfigured:true and a non-exhausted fallback chain when a session is pinned to a fallback model.

Example

if (candidates.length === 1 && isPrimary && effectivePrimary !== configuredPrimary) {
  candidates.push(configuredPrimary);
}

This code snippet illustrates the simpler mitigation approach.

Notes

The proposed fix requires modifying the fallback candidate collection logic, which may have unintended consequences. Thorough testing is necessary to ensure the fix does not introduce new issues.

Recommendation

Apply the simpler mitigation approach by adding the configured primary as a fallback candidate when candidates.length === 1 and isPrimary && effectivePrimary !== configuredPrimary, as it is a more straightforward and less invasive change.

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 - ✅(Solved) Fix Session pin to fallback-only model silently removes fallback chain via dedup (fallbackConfigured:false) [1 pull requests, 1 comments, 2 participants]