Fixing Scheduled Jobs That Do Not Fire or Deliver

A troubleshooting ladder and common failure patterns for scheduled jobs that do not fire or deliver. Useful for operators diagnosing disabled automations and missed runs.

Read this when

  • An automation did not fire at its scheduled time
  • A job ran but nothing arrived in chat
  • Diagnosing timezone or session rollover surprises

A troubleshooting ladder plus the usual failure patterns for scheduled jobs. This page is part of the Automations guide.

Troubleshooting

Command ladder

openclaw status
openclaw gateway status
openclaw automations status
openclaw automations list
openclaw automations runs <jobId> --limit 20
openclaw automations show <jobId>
openclaw logs --follow
openclaw doctor

Automations not firing

  • Inspect the cron.enabled config setting and OPENCLAW_SKIP_CRON in the Gateway's launch environment. Automatic runs can be turned off by either one; to get scheduling working again, clear both disable settings and restart the Gateway.
  • Make sure the Gateway runs without interruption.
  • openclaw automations list --all lists disabled jobs, and openclaw doctor shows the recorded reasons for auto-disable, including terminal one-shot execution failures. Resolve the cause first, then run openclaw automations enable <jobId>. Operator cancellations and restart interruptions still appear in run history, but they add nothing to the consecutive failure count, raise no failure alerts, trigger no automatic repair, and leave no auto-disable record. A job failure that completed before webhook delivery was interrupted still counts. Enabled, unchanged time-scheduled agent-turn jobs that are interrupted before delivery get one retry through cron, one-shots included. Other interrupted one-shots remain disabled so uncertain work is not replayed; review their history before continuing by hand.
  • When a job is disabled with no auto-disable reason, the likely cause is that the conversation which created it was removed or archived, or that it lost its worktree. The skip reason appears in its run history, along with the conversation's name. Restore that conversation, or decide to run without it, before running openclaw automations enable <jobId>.
  • For cron schedules, compare the timezone (--tz) against the host timezone.
  • reason: not-due in run output indicates that the manual run was checked with openclaw automations run <jobId> --due and the job was not yet due.
  • Should the job's execution agent fail to resolve, both automatic and manual attempts record a failed task plus a skipped run-history entry carrying the reason. Pick an agent with openclaw automations edit <jobId> --agent <id>.
  • Automation has no recorded inference account indicates that an older job has not yet adopted a stored account. Run openclaw doctor --fix: it backs up cron state and records the account the job's run would use (either an explicit selection or the first account in the normal account order), without pinning inherited models or changing fallbacks. If several accounts are eligible, Doctor names the others; if the account is unavailable or personal, Doctor names the job and the action required. An authorized operator may explicitly select the intended profile with openclaw automations edit <jobId> --model <provider/model>@<profileId>. A job on an environment or provider-config key keeps that key and needs no adoption; once a stored account becomes eligible, it stops rather than switching. That refusal ends the run without trying fallback models. Neither CLI-native login nor configured credentialless inference requires adopting a host profile; an unbound CLI job cannot select or automatically borrow one. Doctor never infers historical account ownership from the last model in run history.
  • Automation candidate is incompatible with its recorded inference account and route indicates that the selected model belongs to a different provider, or that its runtime relies on a CLI login which cannot replace the job's stored account. Account ownership survives model/default changes. Choose a model and host-managed runtime that work with that account, or deliberately adopt a different stored account with openclaw automations edit <jobId> --model <provider/model>@<profileId>. Ownership is not transferred to a CLI login by a profile selection. Doctor leaves existing bindings as they are; running doctor --fix again will not repair this mismatch. See Inference account continuity.
  • A run may finish ok when an exec call fails and the agent replies anyway. Look for diagnostic: in openclaw automations show <jobId> or diagnostics in run history; unresolved exec failures raise a warning without revealing command arguments.
  • Automation migration is incomplete; run openclaw doctor --fix indicates that a converted monitor still has migration work pending. Finish the repair Doctor reports before running it.
  • If the named creator account stored on a capped job is unavailable, the run fails before any model or tool execution. Job details, run history, and warning logs all name the account. Either re-add it to the channel configuration or recreate the automation from the intended account; changing the delivery --account does not alter creator authority. Legacy jobs that carry no account metadata keep their existing execution policy.

Job fired but no delivery

  • A session queue capacity error means the target cannot accept another turn. Let the work already in progress settle, then retry. Main-session system events keep running until their turn settles; a restart before adoption records a single interrupted error and does not replay the reminder.
  • Delivery mode none means no fallback send from the runner should be expected. When a chat route is available, the agent can still send directly using the message tool.
  • A missing or invalid delivery target (channel/to) means outbound delivery was skipped.
  • On Matrix, copied or legacy jobs whose delivery.to room IDs were lowercased can fail, since Matrix room IDs are case-sensitive. Edit the job so it uses the exact !room:server or room:!room:server value from Matrix.
  • Channel auth errors (unauthorized, Forbidden) mean credentials blocked delivery.
  • Where the dispatcher records intentional suppression, job state, run history, and finished events carry deliverySuppressionReason (empty, silent, heartbeat, or channel_transform). This differs from lastDeliveryError / deliveryError; failures of required delivery also log an error at the moment they occur.
  • For text-only isolated announcements, the text and the fallback queued summary are suppressed only by a completed final that trims to uppercase NO_REPLY. Lowercase forms, wrapped forms, and trailing tokens stay visible. The leading-line form and media behavior are covered in Silent token suppression.
  • If the agent is meant to message the user on its own, confirm the job has a usable route (channel: "last" with a prior chat, or an explicit channel/target).
  • Under delivery.target: "owner", delivery is unavailable when no owner DM resolves; this does not skip execution, nor does it fall back to the last group conversation. delivery.directPolicy: "block" suppresses DM delivery by design. Review and edit the job's delivery settings.

Automations or heartbeat appear to prevent /new-style rollover

  • Freshness for daily and idle resets does not depend on updatedAt; see Session management.
  • The session row may be updated by automation wakeups, heartbeat runs, exec notifications, and gateway bookkeeping for routing/status purposes, but none of these extend sessionStartedAt or lastInteractionAt.
  • For legacy rows created before those fields existed, OpenClaw can recover sessionStartedAt from the transcript JSONL session header while the file is still present. Legacy idle rows that lack lastInteractionAt treat that recovered start time as their idle baseline.

Timezone gotchas

  • When a cron expression omits --tz, the gateway host timezone applies.
  • Schedules set through at that carry no timezone are interpreted as UTC.
  • A job's activeHours holds its own timezone (user, local, or an IANA zone), which is separate from the --tz of the cron expression.
1,184 words · updated Oct 26, 2026