Skip to content

Cron isolated jobs ignore payload.model override — always use agent primary model #58285

@dotwobot-dev

Description

@dotwobot-dev

Bug description

Isolated cron jobs (sessionTarget: "isolated", payload.kind: "agentTurn") ignore the payload.model field and always execute with the agent's primary model.

Expected behavior

Per the cron-jobs docs:

Resolution priority:

  1. Job payload override (highest)
  2. Hook-specific defaults
  3. Agent config default

Setting payload.model: "mistral/mistral-small-latest" on an isolated cron job should make the job run with that model.

Actual behavior

The job always runs with the agent's primary model (e.g. claude-haiku-4-5), regardless of the payload.model value.

Gateway logs show:

live session model switch detected before attempt for <sessionId>:
  mistral/mistral-small-latest -> anthropic/claude-haiku-4-5

Root cause (code analysis)

The issue is a field name mismatch between the cron scheduler and the embedded runtime:

  1. Cron scheduler (runCronIsolatedAgentTurn in gateway-cli-*.js) correctly resolves the payload model and writes it to:

    cronSession.sessionEntry.model = model;           // e.g. "mistral-small-latest"
    cronSession.sessionEntry.modelProvider = provider; // e.g. "mistral"
  2. Embedded runtime (resolveLiveSessionModelSelection in auth-profiles-*.js) reads from different fields:

    const model = entry?.modelOverride?.trim() || defaultModelRef.model;
    const provider = entry?.providerOverride?.trim() || defaultModelRef.provider;
  3. Since modelOverride is never written by the cron scheduler, it's always undefined, so the runtime falls back to defaultModelRef.model (the agent's primary model).

  4. The runtime then detects the difference between the session's model field and the resolved model as a "live session model switch" and switches to the agent default.

Suggested fix

Either:

Option A — Have the cron scheduler also write modelOverride/providerOverride:

cronSession.sessionEntry.providerOverride = provider;
cronSession.sessionEntry.modelOverride = model;

Option B — Have resolveLiveSessionModelSelection fall back to entry.model/entry.modelProvider before defaultModelRef:

const model = entry?.modelOverride?.trim() || entry?.model?.trim() || defaultModelRef.model;

Environment

  • OpenClaw version: 2026.3.28
  • OS: macOS 26.4 (arm64)
  • Node: v25.6.1
  • Models tested: mistral/mistral-small-latest, kimi/k2p5, openai-codex/gpt-5.4
  • All models are in the agents.defaults.models allowlist
  • Agent primary model: anthropic/claude-haiku-4-5

Reproduction

  1. Configure an agent with primary model haiku
  2. Add mistral/mistral-small-latest to agents.defaults.models
  3. Create an isolated cron job with payload.model: "mistral/mistral-small-latest"
  4. Run the job manually with openclaw cron run <jobId>
  5. Check openclaw cron runs --id <jobId> — model will show claude-haiku-4-5
  6. Check gateway logs — will show "live session model switch" from mistral to haiku

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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