Skip to content

fix(agent): agents.defaults.model.fallbacks not inherited by isolated cron sessions #91362

Description

@yanky715

Summary

When agents.defaults.model.fallbacks is configured globally (e.g. ["deepseek/deepseek-v4-flash", "moonshot/kimi-k2.6"]), this fallback list is not inherited by isolated cron sessions when the agent's model config is a plain string.

Expected: Cron sessions should fall back to agents.defaults.model.fallbacks when the agent has no explicit fallbacks configured.

Actual: Cron sessions get an empty fallback chain ([]), causing single-point-of-failure on the primary model.

Root Cause

In resolveSelectedModelFallbacksOverride (agent-scope.ts):

// When agent model config is a string like "deepseek/deepseek-v4-pro":
if (typeof raw === "string") return resolvePrimaryStringValue(raw) ? [] : undefined;
//                                                   ↑ returns [] (empty)
//                 "explicitly no fallbacks" — should be undefined to allow fallthrough

A string model config returns [] (semantically "no fallbacks explicitly set"), which prevents resolveFallbackCandidatesUncached from falling through to global defaults:

// In resolveFallbackCandidatesUncached (model-fallback.ts):
const modelFallbacks = params.fallbacksOverride !== void 0
    ? params.fallbacksOverride           // ← receives [], used as-is
    : resolveAgentModelFallbackValues(params.cfg?.agents?.defaults?.model);  // never reached

Since [] !== undefined is true, the global agents.defaults.model.fallbacks is never consulted.

Full Call Chain

resolveCronFallbacksOverride()                    // run-session-state.ts
  → useSubagentFallbacks = false                  // main agent, not subagent
  → No payload.fallbacks                          // skip priority 1
  → Skip priority 2 (useSubagentFallbacks=false)
  → resolveEffectiveModelFallbacks(hasSessionModelOverride=false)
    → resolveAgentModelFallbacksOverride("main")
      → resolveSelectedModelFallbacksOverride("deepseek/deepseek-v4-pro")
        → returns []  ← BUG: treats string model as "explicitly no fallbacks"

Impact

  • Isolated cron sessions without per-job payload.fallbacks have no model fallback chain
  • The global agents.defaults.model.fallbacks config is silently ignored
  • If the primary model times out or fails, there's no alternative to try

Temporary Workaround

Explicitly set payload.fallbacks on each cron job:

{
  "payload": {
    "kind": "agentTurn",
    "fallbacks": ["deepseek/deepseek-v4-flash", "moonshot/kimi-k2.6"]
  }
}

Proposed Fix

In resolveSelectedModelFallbacksOverride, when the agent model is a plain string (primary ref only, no explicit fallbacks field), return undefined instead of []:

// Before:
if (typeof raw === "string") return resolvePrimaryStringValue(raw) ? [] : undefined;

// After:
if (typeof raw === "string") return undefined;  // let caller fall through to defaults

Or alternatively, in resolveEffectiveModelFallbacks, when hasSessionModelOverride is false, fall through to global defaults:

if (!params.hasSessionModelOverride) {
    return agentFallbacksOverride ?? resolveAgentModelFallbackValues(params.cfg.agents?.defaults?.model);
}

Environment

  • OpenClaw 2026.6.1 (latest at time of report)
  • Agent config:
    {
      "agents": {
        "defaults": {
          "model": {
            "primary": "deepseek/deepseek-v4-pro",
            "fallbacks": ["deepseek/deepseek-v4-flash", "moonshot/kimi-k2.6"]
          }
        },
        "list": [
          { "id": "main", "model": "deepseek/deepseek-v4-pro" }
        ]
      }
    }
  • Isolated cron jobs without explicit payload.fallbacks reproduce the issue; adding payload.fallbacks works around it.

Verification

Confirmed by code inspection of the following source modules in dist/:

  • agent-scope-BcYNOCQG.jsresolveSelectedModelFallbacksOverride returns [] for string models
  • run-session-state-D-DIroy6.jsresolveCronFallbacksOverride priority chain
  • model-fallback-hP2QoTn4.jsresolveFallbackCandidatesUncached checks fallbacksOverride !== undefined
  • isolated-agent-Cw8sZTVo.jsresolveCronModelSelection determines useSubagentFallbacks

Metadata

Metadata

Assignees

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.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions