OpenClaw automation troubleshooting gets much easier once you stop treating every failed scheduled job as “cron broke.” That is rarely the whole story. A job can be disabled before the scheduler sees it. It can be due, then skipped because the execution agent no longer resolves. It can run cleanly and still fail delivery. It can also do exactly what you asked and send nothing because the delivery mode or final output says silence is allowed.
The useful model is a chain: scheduler, eligibility, execution, delivery. Work through it in that order. If you jump straight into logs or rewrite the prompt, you will waste time on the wrong layer.
Start with the automation chain
An OpenClaw automation is not just a cron expression attached to a prompt. The Gateway stores the job, evaluates when it is due, checks whether it can run, starts the payload, records run history, and then handles delivery if delivery is configured. That is a lot of moving parts, but it is also good news. Each part leaves evidence.
I like to split incidents into four questions:
- Did the scheduler run and decide the job was due?
- Was the job eligible to execute with its saved agent, account, tools, and handler?
- Did the payload itself complete?
- Did required output delivery complete?
Those questions keep the investigation honest. A job that never fired is different from a job that fired and produced an empty result. A delivery failure is different from a model failure. A timezone mismatch is different from a disabled Gateway scheduler. Same symptom from the user’s chair, different fix.
The command ladder for OpenClaw automation troubleshooting
The official troubleshooting page gives a clean command ladder. Start here before changing job definitions:
openclaw status
openclaw gateway status
openclaw automations status
openclaw automations list
openclaw automations runs <jobId> --limit 20
openclaw system heartbeat last
openclaw logs --follow
openclaw doctor
That order matters. First confirm the installation and Gateway are healthy. Then confirm automations are enabled. Then inspect the job list and the run history. Only after that should you tail logs or run Doctor. Logs are useful, but they are a bad first move when the run history already tells you whether the problem is scheduling, execution, or delivery.
For a real incident, I usually add two habits. First, copy the job ID from `openclaw automations list` instead of typing it from memory. Second, compare the last expected fire time with `openclaw automations runs <jobId> –limit 20`. If there is no run entry near the expected time, stay in the scheduling layer. If there is a skipped or failed entry, read that result before touching the prompt.
When an automation did not fire
“Did not fire” should mean there is no run entry for the expected occurrence, or the run history says the attempt was skipped before the payload ran. If the job has a successful execution record and the user saw no message, skip ahead to delivery. That is a different incident.
Check disabled scheduler settings
OpenClaw has two obvious scheduler kill switches. The `cron.enabled` config setting can disable automatic runs, and `OPENCLAW_SKIP_CRON` in the Gateway’s launch environment can do the same. If either is set to disable scheduling, clear it and restart the Gateway.
This is the boring fix, which is exactly why it belongs near the top. A disabled scheduler can make every individual job look suspicious. Do not debug ten job definitions when one Gateway setting explains all ten.
Read not-due, agent resolution, and handler failures correctly
`reason: not-due` is not a mysterious scheduler refusal. It means the job was manually checked with `openclaw automations run <jobId> –due`, and the scheduler decided it was not due yet. If you want to force a run, use a normal manual run rather than a due-only check.
If the job’s execution agent cannot be resolved, OpenClaw records a failed task and a skipped run-history entry for automatic and manual attempts. That points to job ownership or agent selection, not the cron expression. Pick a valid execution agent with:
openclaw automations edit <jobId> --agent <id>
`handler-unavailable` is another eligibility failure. It means the heartbeat service was not registered or stopped during the wait, so the attempt is recorded as skipped. Check Gateway startup and sidecar errors before retrying the same job. Re-running the job against the same broken handler will only create more noise.
There is one more failure shape operators often miss: a capped job can fail before model or tool execution if its stored named creator account is unavailable. Job details, run history, and warning logs name the account. Re-add that account to the channel configuration, or recreate the automation from the intended account. Changing the delivery account does not change creator authority.
Check timezone before rewriting the job
Timezone mistakes are the classic scheduled-job trap. In OpenClaw, cron expressions without `–tz` use the Gateway host timezone. `at` schedules without a timezone are treated as UTC. Heartbeat `activeHours` uses configured timezone resolution.
That means “7 AM” can mean three different things depending on how the job was created. If the job is a cron schedule, look for `–tz`. If it is a one-shot `at` schedule, check whether the timestamp carried an offset. If it came from a host image or VPS that changed timezone, inspect the Gateway host timezone before blaming OpenClaw.
Also remember the day-of-month and day-of-week rule. OpenClaw’s cron parsing follows Croner, and Croner defaults to the Vixie cron behavior: if both fields are restricted, the expression matches when either field matches. Linux crontab documents the same behavior. The expression below does not mean “the 15th only when it is Monday”:
0 9 15 * 1
It means 9 AM on every 15th, plus 9 AM on every Monday. In Croner, use the `+` day-of-week modifier when you need both conditions:
0 9 15 * +1
If a job seems to run too often or not when you expected, test the expression before editing the payload. The prompt did not cause that schedule math.
When the job ran but nothing arrived
This is the incident that feels most like a failure to the person waiting in chat. The automation ran. The model may even have produced a useful answer. Still, nothing appeared. OpenClaw’s docs are blunt here: delivery is its own layer.
Delivery mode none
Delivery mode `none` means no runner fallback delivery is expected. The agent can still send directly with the `message` tool when a chat route is available, but the automation runner will not post the final answer for it.
That is a valid design for maintenance jobs, probes, and jobs that write to files or systems instead of chat. It is a bad design for “tell me every morning what happened overnight” unless the prompt explicitly sends its own message through an allowed route. If the run history looks successful and delivery mode is `none`, the first fix is usually the job definition, not the model.
Invalid channel or target
If the delivery target is missing or invalid, outbound delivery is skipped. On hosts with multiple configured channels, isolated announce jobs generally need an explicit `–channel` unless a provider-prefixed target or preserved route selects the channel.
Matrix has a specific footgun: room IDs are case-sensitive. Copied or legacy jobs with lowercased `delivery.to` room IDs can fail. Edit the job to the exact `!room:server` value, or use the `room:!room:server` form from Matrix.
Channel auth errors such as `unauthorized` or `Forbidden` mean credentials blocked delivery. Treat that as channel configuration or account state, not a bad schedule. The run can be fine while the handoff fails at the edge.
Silent and empty output
OpenClaw intentionally suppresses delivery in a few cases. The run history or finished event can record `deliverySuppressionReason` values such as `empty`, `silent`, `heartbeat`, or `channel_transform`. Those are not the same as `lastDeliveryError` or `deliveryError`.
If an isolated run returns only the silent token, `NO_REPLY` or `no_reply`, OpenClaw suppresses direct outbound delivery and the fallback queued-summary path. Nothing is posted back to chat because the result asked to be silent.
This distinction matters when you review agent prompts. “Return NO_REPLY when there is nothing urgent” may be exactly right for a monitor. It is wrong for a daily report where the absence of news should still be visible. In that case, change the prompt to send a small “nothing to report” summary instead of using a silent token.
Delivery failure is not execution failure
The management docs make a useful distinction: run history keeps payload execution in `status` and whole-run completion in `completionStatus`. Required delivery failure can leave execution as `status: “ok”` while completion fails.
This is the line that saves operators from chasing the wrong problem. If execution is ok and completion failed, the payload did its job. Look at delivery route resolution, channel permissions, webhook SSRF policy, target syntax, and failure destinations. Do not rewrite the agent prompt because a webhook refused to send to a private address.
OpenClaw’s outbound automation webhooks also use a strict SSRF guard. Loopback, private, link-local, and other special-use targets are refused by default. If you really need a private receiver, allow only the exact trusted hostname or IP. Broad private-network access should be a deliberate operator decision, not a convenience setting copied from a forum post.
Timezone and session rollover gotchas
Some automation bugs are really expectations bugs. Session rollover is one of them.
OpenClaw session daily and idle reset freshness is not based on `updatedAt`. Automation wakeups, heartbeat runs, exec notifications, and Gateway bookkeeping can update the session row for routing or status, but they do not extend `sessionStartedAt` or `lastInteractionAt`. Daily reset uses when the current session started. Idle reset uses the last real user or channel interaction.
So if automation activity appears to “keep a session alive,” check the actual lifecycle fields before trusting the list view. The docs say background writes do not extend daily or idle reset freshness. That is a good design: a scheduled job should not quietly prevent a conversation from rolling over forever.
For timezone issues, I use a small checklist:
- Does the job use `cron`, `every`, or `at`?
- If it uses `cron`, is `–tz` set?
- If it uses `at`, does the timestamp include a timezone or offset?
- What timezone is the Gateway host using?
- Are day-of-month and day-of-week both restricted?
- Did the job run, but at a time that matches UTC or host time?
That checklist catches most schedule surprises before you touch the job’s payload.
A practical debugging workflow
Here is the workflow I would actually use on a production-ish OpenClaw box.
First, confirm the Gateway and automation system are alive:
openclaw status
openclaw gateway status
openclaw automations status
Second, find the job and inspect its recent run history:
openclaw automations list
openclaw automations runs <jobId> --limit 20
Third, classify the failure. No run near the expected time means scheduler, timezone, or disabled job. A skipped run means eligibility: agent resolution, handler state, creator account, due-only check, or trigger gate. An error status means execution failed. An ok status with failed completion means delivery failed. A succeeded run with suppression means the result was intentionally not delivered.
Fourth, inspect the full job only after you know which layer you are in:
openclaw automations show <jobId>
Look for schedule kind, timezone, payload kind, session target, delivery mode, delivery channel, target, account, tool policy, model override, and failure alert settings.
Fifth, make the smallest fix. Add `–tz` instead of rewriting the cron expression. Fix the Matrix room ID instead of rebuilding the job. Re-add the creator account instead of changing the delivery account. Remove `NO_REPLY` behavior if the user expects a visible report. Keep the change close to the evidence.
Finally, force one manual run and wait when the job must prove completion:
openclaw automations run <jobId> --wait --wait-timeout 10m --poll-interval 2s
Then inspect the new run history. A green manual run does not always prove the next scheduled run will happen at the expected local time, but it does prove payload and delivery can complete under the current definition.
What to fix first by symptom
If every automation is quiet, check `cron.enabled`, `OPENCLAW_SKIP_CRON`, Gateway uptime, and automation status first.
If only one job is quiet, inspect its enabled state, next run, schedule kind, timezone, and recent run history.
If the job is skipped, read the skip reason before running it again. `not-due`, unresolved agent, unavailable handler, and missing creator account point to different fixes.
If the job runs but chat is silent, inspect delivery mode, target syntax, route resolution, auth errors, and suppression reasons.
If a webhook job fails, check the URL, bearer token setup, strict SSRF policy, and whether the receiver expects a payload even when the summary is blank.
If a cron expression fires on more days than expected, check DOM/DOW OR behavior before assuming the scheduler is double-running.
FAQ
Why did `openclaw automations run <jobId> –due` say not-due?
Because `–due` asks the scheduler to run the job only if it is due at that moment. `reason: not-due` means the due check worked. Use a normal manual run when you want to force execution outside the schedule.
Can an OpenClaw automation run successfully and still not send a message?
Yes. Delivery mode can be `none`, output can be intentionally empty, the final result can be `NO_REPLY`, or delivery can be best-effort. Required delivery failure can also leave payload execution ok while whole-run completion fails.
What is the first command to run when a scheduled job fails?
Start with `openclaw automations runs <jobId> –limit 20` once you know the job ID. The recent run history tells you whether you are debugging scheduling, eligibility, execution, or delivery.
Do cron jobs use my local timezone?
Not automatically. Cron expressions without `–tz` use the Gateway host timezone. One-shot `at` schedules without timezone are treated as UTC. Add an explicit timezone when humans will reason about the schedule in local time.
Do automation wakeups prevent session reset?
No. OpenClaw docs say heartbeat, cron, exec, and other background writes do not extend daily or idle reset freshness. Daily reset uses `sessionStartedAt`; idle reset uses `lastInteractionAt`.
Final take
OpenClaw automation troubleshooting is mostly about refusing to collapse four layers into one vague complaint. “The cron job failed” is not enough. Did the scheduler skip it? Did the execution agent disappear? Did the model fail? Did delivery fail after a good run? Did the job intentionally stay silent?
Answer those questions in order and the fix usually becomes small. That is the operator mindset: read the evidence, classify the layer, change one thing, then prove the next run.

