.claude/skills/openclaw-cron-ghost-jobs/SKILL.md
Fix OpenClaw cron jobs running after being removed from jobs.json. Use when: (1) A cron job keeps executing even though it was deleted from jobs.json, (2) Duplicate deliveries (e.g., Julia gets morning briefing twice), (3) Old/legacy jobs firing alongside their replacements after a migration. Root cause is orphan run state files in ~/.openclaw/cron/runs/ that persist nextRunAtMs independently of the job definition.
npx skillsauth add Dbochman/dotfiles openclaw-cron-ghost-jobsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
After removing a cron job from ~/.openclaw/cron/jobs.json, the job continues to
execute on its previous schedule. This causes duplicate deliveries when a new
replacement job is added alongside the still-running old one.
jobs.json but still firesls ~/.openclaw/cron/runs/ shows JSONL files for job IDs not in jobs.jsonOpenClaw's cron subsystem persists run state in per-job JSONL files at
~/.openclaw/cron/runs/<job-id>.jsonl. Each run appends a JSON line with
nextRunAtMs indicating when to fire next. The cron scheduler reads these
files independently — if a run file exists with a future nextRunAtMs, the
job will execute even if its definition is gone from jobs.json.
Removing a job from jobs.json does NOT automatically clean up its run file.
ssh dylans-mac-mini 'python3 -c "
import json, os
with open(\"/Users/dbochman/.openclaw/cron/jobs.json\") as f:
job_ids = {j[\"id\"] for j in json.load(f)[\"jobs\"]}
for f in sorted(os.listdir(\"/Users/dbochman/.openclaw/cron/runs/\")):
run_id = f.replace(\".jsonl\", \"\")
if run_id not in job_ids:
print(f\"ORPHAN: {f}\")
"'
ssh dylans-mac-mini 'python3 -c "
import json, os, time
runs_dir = \"/Users/dbochman/.openclaw/cron/runs/\"
now_ms = int(time.time() * 1000)
for f in sorted(os.listdir(runs_dir)):
with open(os.path.join(runs_dir, f)) as fh:
lines = fh.readlines()
if lines:
last = json.loads(lines[-1])
next_run = last.get(\"nextRunAtMs\", 0)
if next_run > now_ms:
print(f\"ACTIVE GHOST: {f} nextRun={next_run}\")
"'
Delete the orphan run files:
ssh dylans-mac-mini 'trash ~/.openclaw/cron/runs/<orphan-job-id>.jsonl'
No gateway restart is needed — the cron subsystem will simply not find the run file on its next timer tick.
When removing cron jobs from jobs.json, always also delete their run files:
# Remove from jobs.json (edit the file)
# Then clean up the run file
trash ~/.openclaw/cron/runs/<job-id>.jsonl
After cleanup:
# Only active job IDs should have run files
ls ~/.openclaw/cron/runs/
# Compare against:
python3 -c "import json; [print(j['id']) for j in json.load(open('/Users/dbochman/.openclaw/cron/jobs.json'))['jobs']]"
deleteAfterRun: true) clean up their own run files after executionkind: "cron") accumulate run history indefinitelyjobs.json but does NOT scan for orphan run filesdevelopment
Search the web for current information, news, facts, and answers. Use when asked questions about current events, needing to look something up, finding websites, researching topics, or when you need up-to-date information beyond your training data.
development
Summarize any URL, YouTube video, podcast, PDF, or file into concise text. Use when asked to read an article, summarize a link, get the gist of a video or podcast, extract content from a URL, or when you need to understand what a web page or document contains.
development
Play music via Spotify and control Google Home speakers. Use when asked to play music, songs, artists, playlists, podcasts, or control speakers/volume/audio.
testing
Create new OpenClaw skills, modify and improve existing skills, and measure skill performance with evals. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. Also use when asked to "make a skill", "turn this into a skill", "improve this skill", or "test this skill".