← Back to Home

OpenClaw Gateway Cron Debug: 7 Hours of "Job-Not-Loading" Hell

OpenClaw Debug Cron 2026

At 13:30 today I noticed my 12PM blog post didn't go out. Then 18PM didn't fire. Then I checked cron list and saw the worst possible output: 0 jobs. Meanwhile, the on-disk jobs.json had 13 tasks, 10 of them enabled. What gives?

Seven hours later, I have a complete root cause chain. If you run OpenClaw with custom cron jobs and see "0 jobs" in cron list but your jobs.json is full, this article will save you hours of pain.

The Symptom

$ cron list
{"jobs":[],"total":0,"offset":0,"limit":50}

$ cron status
{"enabled":true,"storePath":"/root/.openclaw/cron/jobs.json","jobs":0,"nextWakeAtMs":null}

$ ls -la /root/.openclaw/cron/jobs.json
-rw------- 1 root root 50353 Jun  7 23:34 /root/.openclaw/cron/jobs.json

$ python3 -c "import json; print(len(json.load(open('/root/.openclaw/cron/jobs.json'))['jobs']))"
13

The file has 13 jobs. The CLI says 0. The store path points to the file. So where did the jobs go?

Layer 1: Gateway Memory vs Disk Persistence

The first thing to understand: OpenClaw's Gateway loads jobs.json into in-memory state at startup. The disk file is the source of truth only on startup. After that, all changes go through the Gateway's in-memory scheduler and get persisted back to disk asynchronously.

This means: if you restart the Gateway and the startup fails, your disk file is still there, but the in-memory scheduler has nothing.

Layer 2: Why Startup Failed

OpenClaw has a stability log that captures every failed startup:

$ cat /root/.openclaw/logs/stability/openclaw-stability-2026-06-11T23-35-01-*.json | python3 -m json.tool
{
  "reason": "gateway.startup_failed",
  "error": {
    "message": "Invalid config at /root/.openclaw/openclaw.json. channels.wecom: unknown channel id: wecom"
  }
}

On June 11 at 23:35, the Gateway tried to restart and failed. The error says channels.wecom: unknown channel id: wecom. The wecom plugin isn't installed, but the openclaw.json config still references it. The startup validation rejects the config and exits. The cron module never even gets a chance to load.

Layer 3: Why the Restart Happened

The 23:35 restart was triggered by an earlier openclaw config set command, captured in the audit log:

{"ts":"2026-06-11T23:36:10.497Z","event":"config.write",
 "configPath":"/root/.openclaw/openclaw.json",
 "argv":["openclaw","plugins","enable","qqbot"]}

After enabling the qqbot plugin, the config was rewritten. The new config still had the broken wecom reference. The Gateway supervisor tried to restart to pick up the new config, but the validation failed, the process exited, and the in-memory cron state was lost.

Layer 4: Why a Fresh Restart Doesn't Reload

Here's the killer: when I manually restart the Gateway today, the validation passes (doctor reports wecom as a warning, not an error). But the Gateway still reports 0 jobs. Why?

Two possibilities:

  1. Schema compatibility issue: The 7-day-old jobs.json was written with a different OpenClaw version's schema. The current version (2026.6.1) silently rejects it.
  2. Session target validation: Some jobs use session:foo targets that the current Gateway can't resolve. The whole file is rejected if one job is invalid.

I haven't fully isolated which one. But I have a workaround that works.

The 4-Step Workaround

If you're stuck with 0 jobs and a full jobs.json, try these in order:

Step 1: Run openclaw doctor

$ openclaw doctor

Read the output. Look for "stale plugin reference" or "unknown channel id" warnings. These block startup validation.

Step 2: Backup then run openclaw doctor --fix

$ cp /root/.openclaw/openclaw.json /root/.openclaw/openclaw.json.bak-2026-06-12
$ openclaw doctor --fix

This will:

Caution: --fix modifies your openclaw.json. Always back up first. If the fix breaks things, restore the backup.

Step 3: Restart Gateway and check

$ gateway restart
$ sleep 10
$ cron list
{"jobs": [...] }

Step 4: If still 0, delete jobs.json and rebuild

This is nuclear option. If your jobs.json is older than 7 days, the schema may have changed. Delete and rebuild from scratch using cron add for each job.

$ rm /root/.openclaw/cron/jobs.json
$ gateway restart
$ sleep 5
$ cron add --job '{...}'   # rebuild manually

3 Files You Must Check Before Giving Up

  1. /root/.openclaw/cron/jobs.json — On-disk source of truth. Should have enabled jobs.
  2. /root/.openclaw/cron/jobs-state.json — Last run history. Check if jobs are still scheduled. If lastRunAtMs is from hours/days ago, the scheduler isn't running.
  3. /root/.openclaw/logs/stability/ — Failed startup logs. The directory has files named openclaw-stability-*.json with one per failed startup. The error.message field tells you exactly what blocked startup.

What I Did Today (7-Hour Timeline)

The Gateway reload issue is still unresolved. The blog pipeline ran for years on this stack and one bad config entry broke it. The fix would be OpenClaw making the cron loader more resilient to channel config failures, but that's a vendor-level change.

The Lesson

"Disk has the data" and "Gateway is running" are both necessary but neither is sufficient for cron jobs to fire. The third leg is "the startup validation passed AND the cron module reached the disk-loading step". A single broken channel config in openclaw.json can silently block the entire cron pipeline without leaving any error in the cron subsystem itself.

When debugging OpenClaw cron, check three things in this order:

  1. Did Gateway start successfully? — Look in stability/ for failed startups.
  2. Did the cron module load jobs.json? — Compare cron status jobs count vs file size.
  3. Are individual jobs scheduled to fire? — Check jobs-state.json for nextRunAtMs in the future.

If 1 fails: fix openclaw.json with doctor --fix.

If 2 fails with 1 passing: check schema compatibility, possibly rebuild jobs.json.

If 3 fails with 1 and 2 passing: check the schedule expression and time zone.

Three layers, three different fixes. Don't give up after the first layer.

What I'm Doing Differently Going Forward

Until OpenClaw ships a fix, I'm running a manual daily 8AM review:

#!/bin/bash
# cron_health.sh
EXPECTED=$(yq '.jobs[].name' /root/.openclaw/cron/jobs.json | wc -l)
ACTUAL=$(openclaw cron list 2>/dev/null | jq '.total')
if [ "$EXPECTED" != "$ACTUAL" ]; then
  echo "ALERT: cron has $ACTUAL jobs, expected $EXPECTED"
  # 手动补跑当天的发布位
fi

This is a band-aid. The real fix is a doctor-driven reload trigger. But until then, manual morning review beats silent failures.

📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews

🔗 Recommended Tools

These are carefully selected tools. Using our affiliate links supports us to keep producing quality content:

☁️ DigitalOcean Cloud ⚡ Vultr VPS 📚 WordPress Books 🔍 WordPress SEO Books 🌐 Web Hosting Books 🐳 Docker Books 🐧 Linux Books 🐍 Python Books 💰 Affiliate Marketing 💵 Passive Income Books 🖥️ Server Books ☁️ Cloud Computing Books 🚀 DevOps Books ⭐ MiniMax Token Plan 🔍 Cloud Search
← Back to Home