openclaw - ✅(Solved) Fix bug(doctor): normalizePayloadKind always flags 'agentTurn' jobs as needing normalization (no-op false positive) [1 pull requests, 2 comments, 3 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#43855Fetched 2026-04-08 00:18:34
View on GitHub
Comments
2
Participants
3
Timeline
4
Reactions
1
Author
Timeline (top)
commented ×2closed ×1locked ×1

openclaw doctor --fix and openclaw doctor --non-interactive always report:

Legacy cron job storage detected at ~/.openclaw/cron/jobs.json.
- 42 jobs needs payload kind normalization
Repair with openclaw doctor --fix to normalize the store before the next scheduler run.

Running --fix does not clear the warning. It persists across every subsequent doctor run.

Root Cause

In update-runner-CsfK3SMx.js, the normalizePayloadKind function lowercases the payload kind before comparing:

function normalizePayloadKind(payload) {
  const raw = typeof payload.kind === 'string' ? payload.kind.trim().toLowerCase() : '';
  if (raw === 'agentturn') {
    payload.kind = 'agentTurn';  // writes the SAME value back
    return true;                  // always returns true = always flags as needing normalization
  }
  // ...
}

Any job already using the canonical 'agentTurn' form gets lowercased to 'agentturn', matches the condition, gets written back as 'agentTurn' (no actual change), and is counted as a legacy issue. The fix is a no-op, so the warning never resolves.

Fix Action

Fixed

PR fix notes

PR #44008: fix(cron): normalizePayloadKind returns false for already-canonical payload kinds

Description (problem / solution / changelog)

Problem

openclaw doctor --fix permanently reports every cron job as needing payload-kind normalization, even after repair. The warning never clears.

Fixes #43855

Root Cause

normalizePayloadKind in src/cron/store-migration.ts lowercases the payload.kind string before comparing:

// Before (buggy)
function normalizePayloadKind(payload: Record<string, unknown>) {
  const raw = typeof payload.kind === 'string' ? payload.kind.trim().toLowerCase() : '';
  if (raw === 'agentturn') {
    payload.kind = 'agentTurn'; // no-op — already canonical
    return true;                // always signals "mutated"
  }
  // ...
}

A job with payload.kind = "agentTurn" (canonical form) hits the raw === "agentturn" branch, writes the same value back, and returns true — treating a no-op as a mutation. On the next doctor run the same false positive fires again, so the warning never resolves.

Fix

Add an early-return guard for the two canonical strings before the toLowerCase() comparison:

// After (fixed)
function normalizePayloadKind(payload: Record<string, unknown>) {
  // Already in canonical form — no mutation needed.
  if (payload.kind === 'agentTurn' || payload.kind === 'systemEvent') {
    return false;
  }
  const raw = typeof payload.kind === 'string' ? payload.kind.trim().toLowerCase() : '';
  if (raw === 'agentturn') {
    payload.kind = 'agentTurn';
    return true;
  }
  if (raw === 'systemevent') {
    payload.kind = 'systemEvent';
    return true;
  }
  return false;
}

Non-canonical casing (e.g. "AGENTTURN", "SystemEvent") is still normalised correctly — only the exact canonical strings short-circuit early.

Tests

  • New: does not flag canonical payload kinds as needing normalization — verifies legacyPayloadKind is 0 for jobs already using "agentTurn" / "systemEvent"
  • New: normalizes non-canonical payload kind casing to the canonical form — verifies genuinely non-canonical casing is still fixed and counted
pnpm vitest run src/cron/store-migration.test.ts
✓ src/cron/store-migration.test.ts (4 tests) 4ms

Changed files

  • src/cron/store-migration.test.ts (modified, +65/-0)
  • src/cron/store-migration.ts (modified, +4/-0)

Code Example

Legacy cron job storage detected at ~/.openclaw/cron/jobs.json.
- 42 jobs needs payload kind normalization
Repair with openclaw doctor --fix to normalize the store before the next scheduler run.

---

function normalizePayloadKind(payload) {
  const raw = typeof payload.kind === 'string' ? payload.kind.trim().toLowerCase() : '';
  if (raw === 'agentturn') {
    payload.kind = 'agentTurn';  // writes the SAME value back
    return true;                  // always returns true = always flags as needing normalization
  }
  // ...
}

---

if (payload.kind === 'agentTurn' || payload.kind === 'systemEvent') return false;
// then normalize non-canonical variants
RAW_BUFFERClick to expand / collapse

Summary

openclaw doctor --fix and openclaw doctor --non-interactive always report:

Legacy cron job storage detected at ~/.openclaw/cron/jobs.json.
- 42 jobs needs payload kind normalization
Repair with openclaw doctor --fix to normalize the store before the next scheduler run.

Running --fix does not clear the warning. It persists across every subsequent doctor run.

Root Cause

In update-runner-CsfK3SMx.js, the normalizePayloadKind function lowercases the payload kind before comparing:

function normalizePayloadKind(payload) {
  const raw = typeof payload.kind === 'string' ? payload.kind.trim().toLowerCase() : '';
  if (raw === 'agentturn') {
    payload.kind = 'agentTurn';  // writes the SAME value back
    return true;                  // always returns true = always flags as needing normalization
  }
  // ...
}

Any job already using the canonical 'agentTurn' form gets lowercased to 'agentturn', matches the condition, gets written back as 'agentTurn' (no actual change), and is counted as a legacy issue. The fix is a no-op, so the warning never resolves.

Expected Behavior

Jobs already using the canonical 'agentTurn' or 'systemEvent' form should not be flagged. normalizePayloadKind should compare against the canonical mixed-case form first and return false (no mutation needed):

if (payload.kind === 'agentTurn' || payload.kind === 'systemEvent') return false;
// then normalize non-canonical variants

Steps to Reproduce

  1. Have any cron jobs with payload.kind: 'agentTurn'
  2. Run openclaw doctor --non-interactive
  3. Observe "X jobs needs payload kind normalization" warning
  4. Run openclaw doctor --fix
  5. Run doctor again — warning is still present

Environment

  • OpenClaw version: 2026.3.11 (29dc654)
  • macOS 15.3 (Darwin 25.3.0, arm64)
  • Node.js v25.8.0
  • 42 cron jobs, all with payload.kind: 'agentTurn'

extent analysis

Fix Summary

Correct normalizePayloadKind so it does not flag already‑canonical payload kinds and only returns true when a real mutation occurs.


Fix Plan

1. Update the normalization function

Edit src/update-runner-CsfK3SMx.js (or the appropriate source file) :

// before
function normalizePayloadKind(payload) {
  const raw = typeof payload.kind === 'string' ? payload.kind.trim().toLowerCase() : '';
  if (raw === 'agentturn') {
    payload.kind = 'agentTurn';  // writes the SAME value back
    return true;                  // always returns true = always flags as needing normalization
  }
  // …
}
// after – idempotent and explicit
function normalizePayloadKind(payload) {
  // Guard against missing/invalid kind
  if (typeof payload.kind !== 'string') return false;

  const original = payload.kind.trim();

  // 1️⃣ Fast‑path: already canonical – nothing to do
  if (original === 'agentTurn' || original === 'systemEvent') {
    return false;               // no mutation, not a legacy job
  }

  // 2️⃣ Normalise lower‑cased variants
  const lowered = original.toLowerCase();

  if (lowered === 'agentturn') {
    payload.kind = 'agentTurn';
    return true;                // mutated
  }

  if (lowered === 'systemevent') {
    payload.kind = 'systemEvent';
    return true;                // mutated
  }

  // 3️⃣ Unknown kind – leave untouched (or throw if you prefer)
  return false;
}

Key points

  • Compare against the canonical mixed‑case strings first.
  • Return false when no change is made.
  • Keep the lower‑casing step only for the non‑canonical branch.

2. Run the migration script (optional but recommended)

If you want to clean the existing store in one go without invoking doctor repeatedly:

node -e "
const fs = require('fs');
const path = require('path');
const storePath = path.join(require('os').homedir(), '.openclaw/cron/jobs.json');
const jobs = JSON.parse(fs.readFileSync(storePath, 'utf8'));

let changed = 0;
jobs.forEach(j => {
  if (normalizePayloadKind(j.payload)) changed++;
});

if (changed) {

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