Skip to content

Bug: Cron Control UI cannot clear saved agentTurn model/thinking overrides by blanking fields #96287

Description

@takamasa-aiso

Summary

Clearing the Model and Thinking/Effort fields in the Cron Control UI does not remove previously saved overrides. After saving and reopening the cron job, the old values reappear.

This only shows up on cron jobs that already have payload.model and/or payload.thinking saved. Jobs without existing overrides appear to save blank fields correctly because there is no previous value to preserve.

Environment

  • OpenClaw version: 2026.6.9
  • Surface: Cron Control UI
  • Cron type: isolated agentTurn job

Steps to reproduce

  1. Create or edit an isolated agentTurn cron job with explicit overrides, for example:
    • payload.model = openai/gpt-5.5
    • payload.thinking = high
  2. Open the cron job in the Control UI editor.
  3. Clear the Model field and the Thinking/Effort field.
  4. Save the job.
  5. Reopen the job in the UI, or inspect it with openclaw cron get <id>.

Expected behavior

The cleared fields should remove the stored overrides so the cron job inherits the default model/thinking settings.

Expected update behavior:

  • blanking a previously stored payload.model should send an explicit clear value, such as payload.model: null
  • blanking a previously stored payload.thinking should send an explicit clear value, such as payload.thinking: null

Expected persisted job after the update is merged:

  • payload.model is removed
  • payload.thinking is removed

Actual behavior

The previous values are preserved. Reopening the editor shows the old Model and Thinking/Effort values again.

Suspected cause

The Control UI appears to omit empty Model and Thinking/Effort fields from the cron update patch instead of sending an explicit clear operation.

Because cron updates are partial/merge-based, omitted nested payload fields preserve existing stored values. This makes a cleared field indistinguishable from an unchanged field.

There also appears to be an inconsistency in the cron patch handling:

  • payload.model: null has an explicit delete path
  • payload.thinking: null appears to be filtered out by normalization and is not handled by the payload merge logic

In v2026.6.9, the payload merge logic deletes model when patch.model === null, but only updates thinking when it is a string. The cron patch schema/normalizer also appears to treat thinking as an optional string, so thinking: null may be dropped before it reaches the merge logic.

Possible fix

This likely needs changes in both the Control UI and cron patch handling.

The UI fix alone is sufficient for model only if the existing backend model:null path is preserved. For thinking, the backend patch type, exported API type, normalizer, and merge logic also need to accept and preserve null through to the delete branch.

1. Control UI

When editing an existing agentTurn cron job, blanking a field that previously had a saved override should send an explicit clear value instead of omitting the key.

The UI should only send null for fields that are actually stored on the existing cron payload. If the editor displays inherited/default values, those displayed values must not be mistaken for saved overrides.

Pseudocode:

const payload: CronPayloadPatch = { kind: "agentTurn" };

const model = modelInput.trim();
if (model) {
  payload.model = model;
} else if (currentJob.payload?.kind === "agentTurn" && "model" in currentJob.payload) {
  payload.model = null;
}

const thinking = thinkingInput.trim();
if (thinking) {
  payload.thinking = thinking;
} else if (currentJob.payload?.kind === "agentTurn" && "thinking" in currentJob.payload) {
  payload.thinking = null;
}

The important part is preserving user intent: blanking a previously-set field should mean “clear this override”, while an omitted key should continue to mean “leave this field unchanged”.

2. Cron patch schema / exported type / normalizer

Allow payload.thinking in an agentTurn patch to be either a string or null, matching the existing clear behavior for payload.model.

Pseudocode:

thinking: Type.Optional(Type.Union([Type.String(), Type.Null()]))

and in normalization:

if ("thinking" in next) {
  if (next.thinking === null) {
    next.thinking = null;
  } else {
    const thinking = parseOptionalField(TrimmedNonEmptyStringFieldSchema, next.thinking);
    if (thinking !== undefined) next.thinking = thinking;
    else delete next.thinking;
  }
}

3. Cron payload merge

Treat payload.thinking: null as a delete/clear operation, matching payload.model: null.

Pseudocode:

if (typeof patch.thinking === "string") {
  next.thinking = patch.thinking;
} else if (patch.thinking === null) {
  delete next.thinking;
}

4. Optional CLI parity

Consider adding openclaw cron edit <job-id> --clear-thinking for parity with --clear-model.

This is not required to fix the Control UI bug, but it would make the API, UI, and CLI behavior consistent.

Suggested tests

  • Updating an existing agentTurn cron with payload.model: null removes the stored model override.
  • Updating an existing agentTurn cron with payload.thinking: null removes the stored thinking override.
  • Updating with omitted model / thinking still preserves existing values.
  • normalizeCronJobPatch preserves payload.thinking: null instead of dropping it.
  • API/schema validation accepts payload.thinking: null in a cron update patch.
  • Store-level update persists removal of an existing payload.thinking value when patched with thinking: null.
  • Control UI saving a previously-set Model field after clearing it sends an explicit clear.
  • Control UI saving a previously-set Thinking/Effort field after clearing it sends an explicit clear.
  • Control UI does not send null for a blank field on a new job or for a field that only displayed an inherited/default value.
  • Existing payload.model: null behavior remains covered as a regression test.

Related issues / PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.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