You set up a cron job. It ran twice in a row, never ran at all, or it fired at the wrong time. Nothing in the logs explains why. The most likely cause is not a bug. OpenClaw has three different schedule types that behave very differently from each other, and most operators pick one by copying an example without fully understanding what they chose. The difference matters more than most people expect: an at job that runs once, an every job that fires on an interval regardless of clock time, and a cron job that targets a specific time of day all have distinct failure modes. This article explains each type, covers the most common failure modes, and shows you exactly how to verify that a job is scheduled correctly before relying on it.
TL;DR
Ran twice: almost always a duplicate job entry. Check for two jobs with the same or similar schedule. Never ran: either the job is disabled, the schedule expression is wrong, or a timezone mismatch means the trigger time has not arrived yet in UTC even though it has in your local time. Wrong time: timezone. OpenClaw cron jobs use UTC by default unless you explicitly set a timezone in the schedule. If your cron expression says 8:00 and you are in US Eastern, it is firing at 3am or 4am your time, not 8am.
Every indented block in this article is a command you can paste directly into your OpenClaw chat. Your agent will run it and report back. You do not need to open a terminal, edit any files manually, or navigate any filesystem.
The three schedule types in OpenClaw and when to use each
OpenClaw offers three distinct schedule types. Understanding which one you have and what it actually does is the starting point for diagnosing almost every scheduling problem.
List all my current cron jobs. For each one, show me: the job name or ID, the schedule type (at/every/cron), the full schedule expression, whether it is enabled or disabled, and when it last ran.
at: one-shot at a specific time
The at type runs a job exactly once at a specific timestamp. Once it fires, it is done. It will not run again unless you create a new job or reset it manually. This is the right type for one-time tasks: send a specific message on a date, trigger a setup action once, run a migration at a specific window.
The failure mode for at jobs: they never fire because the timestamp is in the past or in a timezone that was misinterpreted. Check the timestamp in ISO-8601 format and confirm it is UTC if no timezone is specified.
I have an “at” type cron job that never fired. What is the scheduled timestamp in my config? Convert it to my local timezone and tell me whether it is in the future or the past. If it is in the past, did it fire and I missed it, or did it never fire at all?
every: recurring interval
The every type runs a job repeatedly on a fixed millisecond interval: every 30 minutes, every 6 hours, every 24 hours. The interval is measured from when the job was last triggered, not from a fixed time of day. A job set to every 24 hours that was first triggered at 3pm will trigger at 3pm every day, not at midnight.
The failure mode for every jobs: two jobs set to the same interval that were both triggered at the same time will fire simultaneously on every cycle. This is the most common cause of “ran twice.” Check whether you have duplicate jobs with the same interval.
A less obvious failure mode: an every job with a very short interval (every 5 minutes) running a task that takes longer than 5 minutes to complete. If the previous run has not finished when the next interval fires, behavior depends on your OpenClaw version, but it can result in overlapping runs or skipped intervals. For tasks that might run long, use a longer interval or add a lock mechanism.
Do I have any “every” type cron jobs with the same or similar intervals that could be firing at the same time? Do any of my “every” jobs have an interval shorter than the typical runtime of the task they trigger? Show me any potential conflicts.
cron: expression-based schedule
The cron type uses a standard cron expression to define when a job fires. A five-field cron expression specifies minute, hour, day of month, month, and day of week. This is the most flexible schedule type and the one most likely to be misconfigured.
The most important thing to know about cron-type jobs in OpenClaw: they run in UTC unless you explicitly set a timezone in the schedule. If your cron expression is 0 8 * * * (8am every day) and you are in US Eastern Standard Time (UTC-5), your job fires at 3am your local time, not 8am as intended. In US Eastern Daylight Time (UTC-4), it fires at 4am your local time. If you set up the job in winter and the time seems off in summer, daylight saving is exactly why.
For each of my cron-type jobs, what timezone is configured? If no timezone is set, what time does the cron expression correspond to in my local timezone (America/New_York)? Is there any job that I intended to run at a specific local time but that is actually firing at a different time due to UTC default?
Why the job ran twice
A cron job that ran twice in quick succession almost always has one of three causes. Work through them in order.
Duplicate job entries
The most common cause by far. When you create a cron job by asking your agent, and then ask again (either repeating the request or adjusting the parameters), two separate job entries can end up in your config. Both are enabled, both have the same or similar schedule, and both fire.
Show me all cron jobs in my config sorted by schedule. Are there any that have identical or nearly identical schedules and similar payloads? I think I may have created a duplicate.
If you find duplicates, disable the one you want to remove, verify the remaining job runs correctly once, then delete the disabled one. Do not delete both and recreate; delete only the duplicate.
Gateway restart triggering immediate execution
In some OpenClaw versions, restarting the gateway can trigger certain cron jobs to run immediately if they were overdue according to the schedule. If you recently restarted your gateway and a job ran twice close together, this is the likely cause. The job ran once at its scheduled time and once again immediately after the restart caught it as overdue.
When did my gateway last restart? Was there a cron job run within a few minutes of that restart? Is that the source of the double run I observed?
Overlapping intervals on every-type jobs
An every job measures its interval from the last trigger. If the clock drifts, the server is paused briefly (rare but possible on VMs), or a restart shifts the anchor time, the next trigger can land very close to a manual trigger you ran for testing. Check the run history timestamps to see whether the two runs were genuinely at the scheduled interval or at an unusual spacing.
Show me the last five runs of [job name]. What were the exact timestamps? Is the spacing between runs consistent with the configured interval, or does one pair of runs look like it fired unusually close together?
Why the job never ran
The job is disabled
The first thing to check. A job that is disabled will not run regardless of what the schedule says. Jobs can end up disabled if you disabled them for testing and forgot to re-enable, or if an error during creation left them in a disabled state.
Check whether the cron job [name or ID] is enabled. If it is disabled, when was it last enabled and why was it disabled?
The schedule expression is wrong
Cron expressions are easy to get wrong. An off-by-one in the hour field, a month expressed as a name instead of a number, or a day-of-week value that points to a day that comes rarely. The most common mistake is confusing the field order and putting the hour value in the minute field, which means the job is set to fire at a specific minute of every hour rather than a specific hour of every day.
Verify this cron expression for me: [paste expression]. What day and time does it represent in human language? Is that what I intended? When will it next fire in UTC, and what is that time in America/New_York?
Timezone mismatch
Already covered under the cron type explanation, but worth repeating as a standalone failure mode. If you see your job running at an unexpected time rather than not running at all, this is almost certainly timezone. If you see it not running at the time you expected, verify whether it already fired at the UTC-equivalent time.
My cron job is set to run at 8am. In what timezone? What time did it actually fire according to the run history? Convert that run time to America/New_York and tell me what local time the job is actually running.
The gateway was down when the job was scheduled to fire
If your OpenClaw gateway restarts or is down during a scheduled trigger window, the job may be skipped or deferred depending on your version. Check whether the gateway had any downtime or restarts around the time the job was supposed to run.
Did the OpenClaw gateway restart or experience any downtime in the 24-hour window when my cron job failed to run? Check the gateway logs for any restarts or errors around [time].
How to verify a job is scheduled correctly
Before relying on a cron job for anything important, verify that it will fire when you expect it to. Do not trust that the creation command succeeded without checking.
For the cron job I just created: when will it next fire? Express the time in both UTC and America/New_York. Is the job currently enabled? Is there any other job in my config with the same or similar schedule that could conflict with it?
For a new job, also run a manual trigger before the first scheduled run to confirm the job payload works correctly. A job that runs successfully on manual trigger but fails on schedule usually has a timing or payload issue, not a schedule issue.
Trigger my cron job [name] manually right now and show me the full output. I want to verify it works before relying on the scheduled run.
Getting the schedule right for common use cases
Rather than writing cron expressions from scratch, these are the correct expressions for the most common OpenClaw scheduling patterns.
Daily morning brief at 8am US Eastern
You need to account for daylight saving. The safest approach is to set your schedule timezone explicitly to America/New_York rather than hard-coding a UTC offset that changes twice a year.
schedule: { kind: "cron", expr: "0 8 * * *", tz: "America/New_York" }
With the timezone set, the job fires at 8am New York time regardless of whether it is EST or EDT.
Every 15 minutes, every hour
Use the every type with milliseconds:
15 minutes: { kind: "every", everyMs: 900000 }
1 hour: { kind: "every", everyMs: 3600000 }
6 hours: { kind: "every", everyMs: 21600000 }
24 hours: { kind: "every", everyMs: 86400000 }
Weekdays only at 9am Eastern
schedule: { kind: "cron", expr: "0 9 * * 1-5", tz: "America/New_York" }
First of each month at midnight UTC
schedule: { kind: "cron", expr: "0 0 1 * *" }
Create a cron job that runs every day at 8am America/New_York. Use the cron schedule type with the timezone explicitly set. The job should [describe payload]. Confirm the schedule before creating it.
Reading the run history to diagnose scheduling problems
The run history is your primary diagnostic tool for any scheduling problem. It shows when jobs actually ran, whether they succeeded, and what output they produced. If something is wrong with a schedule, the run history tells you what happened and when.
Show me the run history for my cron job [name]. How many times has it run, when did it last run, and did it succeed or fail each time? If it failed, what was the error?
Run history also helps you verify that the interval between runs is consistent. Consistent intervals mean the scheduler is working correctly. Irregular intervals mean something is interfering: gateway restarts, system clock issues, or competing jobs blocking the scheduler.
For my cron job [name], look at the last 10 runs. What is the average interval between runs? Are any intervals significantly longer or shorter than the configured schedule would predict? What do the outliers correspond to?
Keeping your cron jobs organized
As you add cron jobs over time, keeping track of what is running becomes a maintenance task in its own right. An unreviewed cron job list tends to accumulate disabled jobs, duplicate jobs, and jobs that were created for a temporary purpose and never removed.
Do a full audit of my cron jobs. List all jobs including disabled ones. For each one: what does it do, when does it run, is it currently enabled, when did it last run, and is it still serving a purpose? Flag any that look like duplicates, any that have never run, and any that were probably created for a temporary purpose and should be deleted.
Monthly cron audit
Add a monthly cron job whose only purpose is to audit your other cron jobs and send you a summary. Job count, last-run timestamps, any that failed in the last month, any that have not run at all. Three minutes of reading once a month prevents the kind of silent failures that only get noticed when something important does not happen when it was supposed to.
The monthly audit cron job is a good first cron job for operators new to automation. It requires nothing from the target system and does not modify any configuration. It just reads the current cron job state and reports. Creating it before building more complex jobs gives you a safety net that will surface duplicates, misconfigured schedules, and silently failing jobs before they become a problem.
Create a monthly cron job that runs on the first of each month at 9am America/New_York. It should: list all cron jobs including disabled ones, report which ones have not run in over 30 days, report any failures in the last month, and send me the full summary as a Telegram message.
Why the job ran at the wrong time
A cron job that runs but at an unexpected time points almost exclusively to timezone misconfiguration. The diagnostic is straightforward: compare when the job actually ran against when you intended it to run, then convert both to UTC and see whether they match.
My cron job is running at the wrong time. The last run timestamp was [time]. I expected it to run at [expected time] in my timezone. Convert both times to UTC and compare them. What UTC time is my cron expression actually targeting?
Daylight saving shifts
If a cron job was working correctly for months and then started running an hour early or an hour late, daylight saving is the cause. OpenClaw’s UTC default does not shift with daylight saving. Your local time shifts. If your cron expression was set to the UTC equivalent of your local time in winter, it is now off by one hour in summer because the UTC offset changed.
The permanent fix is to set tz explicitly in your schedule to a named timezone like America/New_York. Named timezones account for daylight saving automatically. Hard-coded UTC offsets do not. This applies to every cron-type job in your config, not just the ones that seem affected. Any job using a UTC offset instead of a named timezone will shift an hour twice a year. Fix them all at once during a config review rather than discovering them individually when daylight saving catches you off guard.
Update my cron job [name] to use America/New_York as the explicit timezone rather than relying on UTC offset. Keep the intended local time the same. Show me the updated schedule configuration before applying it.
The wrong “every” anchor time
An every-type job fires based on when it was first triggered, not on a fixed time of day. If you created a daily job at 3pm hoping it would run at 3pm every day, it will. But if you re-created the job at 11am for some reason, it now fires daily at 11am. The job runs at the right interval but the anchor time changed.
If you need an every-type job to anchor to a specific time of day, create it at that time or use a cron expression instead, which is clock-based rather than interval-based. The practical advice: use the cron schedule type with an explicit timezone whenever the time of day matters. Use the every type when you only care about frequency and the anchor time is irrelevant.
How to test a cron job before relying on it
Manual trigger testing is the fastest way to verify a job works before waiting for the scheduled run. For a new or recently changed job, always run a manual trigger first.
Run my cron job [name] manually right now. Show me the full output of the run including any tool calls, API responses, and the final result. I want to verify it works correctly before the next scheduled run.
If the manual trigger works but the scheduled run does not, the problem is in the schedule configuration or the session context available at trigger time. Scheduled sessions may not have the same context as an interactive session where you manually triggered the job. Common differences:
- A scheduled session reads workspace files from disk at the time of the trigger. If a workspace file was modified after the job was created, the scheduled run sees the new version, which may behave differently.
- Channel tokens configured in an interactive session may not be available in a headless scheduled run if they were not written to the config permanently.
- Model settings in the cron job payload override the default model for that run. If the model specified in the payload is unavailable, the job will fail differently than an interactive session that falls back gracefully.
My cron job works when I trigger it manually but fails on the scheduled run. What differences exist between the context available to a manual trigger versus a scheduled run? Does the job payload reference anything that might not be available in a headless scheduled session?
Setting up failure alerts for cron jobs
A cron job that runs silently and fails is worse than one that never runs. At least a job that never runs is obviously broken. A job that runs and fails silently appears to be working until you check the output and realize it has been producing nothing for three days.
For each of my cron jobs, add failure handling: if the job fails for any reason, send me a Telegram message with the job name, the time of failure, and the error message. I should know immediately when a scheduled task breaks, not the next morning.
For jobs that produce output you need to review, also add a success notification that includes a brief summary. A daily morning brief that silently produces an empty output is still a failure even if no error was thrown. The notification confirms the job ran and produced useful content.
For my [specific job] cron job: after it runs successfully, send me a one-line Telegram message with the time it ran and a brief summary of what it produced. After any failure, send me the error. I want a notification either way so I always know it ran.
Alert fatigue
A notification for every cron run gets ignored quickly. The right approach is: notifications for failures always, notifications for successes only on jobs where you need to review the output. A queue processor that runs every 15 minutes should alert on failure but not send you a success notification 96 times a day. A daily morning brief should send its content as the notification so you can read it without opening another window.
Advanced patterns: chaining, locking, and conditional execution
Preventing overlapping runs with a lock file
For jobs that might occasionally run longer than their interval, a lock file prevents overlapping runs. The job checks for the lock file at start, creates it if it does not exist, runs the task, and deletes the file on completion.
Update my cron job [name] to use a lock file to prevent overlapping runs. At the start of each run, check whether workspace/locks/[job-name].lock exists. If it does, skip this run and log “previous run still in progress.” If it does not, create the file, run the task, then delete the file on completion regardless of whether the task succeeded or failed.
Conditional execution based on time or day
Some jobs should only run under certain conditions: only on business days, only when a flag file exists, only when a counter has reached a threshold. OpenClaw does not have built-in conditional scheduling, but you can add condition checks to the job payload itself.
Update my cron job [name] to only run on weekdays. At the start of each run, check whether today is Monday through Friday. If it is a weekend, log “weekend – skipping” and exit without running the task.
Chaining jobs via file flags
When job B should only run after job A has completed successfully, use a flag file. Job A writes a flag file on success. Job B checks for the flag file before running. If the flag does not exist, job B skips and tries again at the next interval.
I want job B to only run after job A has completed. Update job A to write workspace/flags/job-a-complete.flag on success. Update job B to check for that file at the start of each run, and skip if it does not exist. After job B completes successfully, it should delete the flag file so job A needs to run again before job B can run again.
Common questions
My cron job runs at the right time but does nothing. How do I debug the payload?
Trigger it manually and watch the output. If it does nothing on manual trigger, the problem is in the payload, not the schedule. If it does something on manual trigger but nothing on schedule, check whether the payload references any context that is available in an interactive session but not in a scheduled run: a user preference that only exists in a specific session, a file path relative to a session working directory, or a channel configuration that is not set up for automated messages. Scheduled runs have access to your workspace files and config but not to any specific interactive session’s context.
I want the same job to run at different times on weekdays versus weekends. How do I set that up?
Create two separate jobs: one with a weekday cron expression and a different schedule, one with a weekend cron expression. There is no single cron expression that runs at different times on different days of the week. Two jobs with the same payload and different schedules is the clean solution.
How do I pause a cron job temporarily without deleting it?
Disable it. A disabled job stays in your config with all its settings intact but does not fire. When you want to resume it, enable it again. The schedule resumes from the current time, not from when it was paused. For an every-type job, disabling and re-enabling effectively resets the interval anchor to the re-enable time.
My daily brief cron job was working fine for weeks and then stopped. What changed?
Most common causes in roughly this order: the job was accidentally disabled (check first); a gateway update changed how cron scheduling works; a config change to the model routing or channel configuration broke the job payload; the API key or channel token used by the job expired; the workspace file the job reads was deleted or renamed. Check the run history first to see exactly when it stopped and whether the last few runs show any errors. The error message in the last failed run usually points directly at the cause.
Can a cron job trigger another cron job when it finishes?
Not natively through the scheduler. The scheduler fires jobs based on time, not on completion of other jobs. The workaround is task chaining: the first cron job writes an output file, and the second cron job checks for that file before running. This is the file handoff pattern from the task chaining article. The schedule timing needs to account for how long the first job takes so the second does not run before the first has written its output.
How many cron jobs can I run simultaneously without causing problems?
OpenClaw processes cron jobs one at a time in the main session or in isolated sessions depending on the job type. Jobs scheduled to fire at the same time queue rather than running in parallel. If you have many jobs scheduled to fire at the same time (for example, all at midnight), the later jobs in the queue may run noticeably after their scheduled time. Stagger high-volume job schedules by a few minutes to avoid queuing at the same time. A simple rule: no two jobs at the same minute mark within the same hour. If you have a suite of daily jobs, schedule them at :00, :02, :04, :06 rather than all at :00. This spreads load evenly and makes the run history much easier to read because each job appears as a clearly separate entry rather than a cluster of simultaneous triggers.
What happens if the server running OpenClaw goes offline during a scheduled trigger?
The job is missed. OpenClaw does not have a built-in catch-up mechanism for missed scheduled jobs as of March 2026. When the gateway comes back online, the scheduler resumes from the current time. An every-type job will fire at the next interval from resume time, not at the missed intervals. A cron-type job fires at its next scheduled time, skipping any missed triggers. If you need guaranteed execution, add a monitoring check that verifies the job ran and alerts you if a scheduled window was missed.
Can I run a cron job at a specific second, not just a specific minute?
Standard five-field cron expressions go to minute-level precision. If you need sub-minute scheduling, use the every type with a millisecond interval. For example, every 30 seconds is everyMs: 30000. For most use cases, minute-level precision is sufficient and easier to reason about.
Can I set different models for different cron jobs?
Yes. The cron job payload can specify a model override that applies only to that job’s run. A simple queue check does not need Claude Sonnet. Route it to a local model or DeepSeek Chat to reduce cost. A complex research job that needs strong reasoning can specify the more capable model. Set the model in the job payload rather than the global config so it only applies to that specific job and does not affect interactive sessions or other cron jobs.
My cron job sends a Telegram message but the message is always empty. Why?
Almost always a problem with how the payload captures and passes the output. Check whether the job is running the task, getting a result, and then explicitly passing that result to the Telegram send command. If the job runs the task and sends a notification separately without explicitly passing the task output, the notification has no content to include. The job payload needs to: run the task, capture the output in a variable or file, then use that output in the send command. Ask your agent to trace through the last job run step by step and identify where the output gets dropped.
Is there a way to see what cron jobs are scheduled to run in the next 24 hours?
Ask your agent directly: “List all my enabled cron jobs and tell me when each one is next scheduled to fire in the next 24 hours, in America/New_York time.” The agent can calculate the next trigger time for each job based on its schedule type and expression. This is useful before going offline overnight to confirm that the right jobs are queued and no surprises are coming.
What is the maximum number of cron jobs I can run without performance issues?
There is no hard limit documented as of March 2026, but practical experience suggests that the scheduler becomes harder to reason about above 20-30 active jobs. More importantly, if many jobs fire at overlapping times and each one spawns an isolated session, you may see increased API costs and slower response times during those windows. Keep the job count proportional to your actual needs. A setup with 50 cron jobs where 40 of them are rarely needed is a maintenance burden that also makes the 10 important jobs harder to monitor effectively.
I accidentally deleted a cron job. Can I recover it?
If you have git history of your workspace (which you should), check the recent commits for the cron job configuration before it was deleted. If the job was created by asking your agent and the agent committed the config change, the previous config state should be in git history. If you do not have git history, you will need to recreate the job from memory. This is one of the concrete reasons why git commits after every config change are worth the habit: recovering deleted jobs from git history takes 30 seconds.
My cron job runs correctly in testing but produces different output in production. Why?
Check for environmental differences: different workspace files loaded in the two contexts, different model routing (test might be using a more capable model than production), different API keys or channel tokens, or the test was run as an interactive manual trigger while production is a headless scheduled run. The most common cause is that an interactive test session had workspace files with a more recent version of instructions than the scheduled job sees. Verify that your workspace files are committed and that the scheduled run is picking up the current versions.
Cron Commander
The complete scheduling reference for OpenClaw
Every schedule type with working examples, common patterns pre-built (morning brief, weekly digest, queue processor, memory cleanup), failure alert setup, and the run history monitoring pattern. Drop it into your agent and it handles the setup.
