How Automations Work: Runtime, Lifecycle, and Scheduling
Explains how the Gateway scheduler executes automation jobs, the state retained across runs, and how a repeated request becomes a stored schedule. For operators and developers debugging or designing automations.
Read this when
- Deciding whether automations fit a scheduled workload
- Debugging run lifecycle, catch-up, or task reconciliation
- Turning a repeated request into a stored schedule
How the Gateway scheduler executes a job, the state it retains across runs, and the path from a repeated request to a stored schedule. This page is part of the Automations guide.
How automations work
- Automations execute within the Gateway process, not within the model. Schedules only fire while the Gateway is running.
- OpenClaw's shared SQLite state database stores job definitions, runtime state, and run history, so a restart does not drop schedules.
- Cron run history holds the record of every automation run.
- Main-session system events go through normal session admission and keep running until the exact turn settles. A restart before adoption logs a single interrupted failure; it neither reports success nor replays the reminder.
- A running automation may delete itself via its scoped automation tool and still complete its final reply. If another turn removes or disables it, or Stop is pressed, the active run is still cancelled.
- Agent, command, and script jobs write their result once into the chat or topic that receives it, including jobs that have no creating conversation. When a different delivery destination is configured, that destination gets the result instead of the creating chat; with no external route, delivery goes to the creating conversation. Execution context stays separate. See Automation delivery.
- One-shot jobs (
--at) delete themselves automatically once they complete successfully: delivery is confirmed, not requested, intentionally suppressed, or explicitly best-effort. If required delivery fails or ends in an unknown state, the job is kept disabled for inspection and the payload is not replayed. Add--keep-after-runto retain successful jobs as well. - Per-run wall-clock budget:
--timeout-secondswhen it is set. If not, the scheduler's own 60-minute watchdog bounds isolated/detached agent-turn jobs, command jobs get 10 minutes by default, and script payloads get 5 minutes by default. This scheduler deadline stands apart from the per-model-attempt budget (agents.defaults.timeoutSeconds, default 48 hours): beginning a fallback does not reset it. - At Gateway startup, overdue jobs that need agent execution are rescheduled rather than replayed at once, which keeps model/tool execution out of scheduler startup. Migrated monitors and main-session system events are included. Startup catch-up delays survive label or payload-content reconciliation and further restarts; a schedule change starts a new scheduling decision.
- Jobs whose agents are still preparing stay queued and do not block healthy agents. Once their Gateway finishes preparation, they resume.
- Cron slots that elapse during a successful or skipped scheduled run are not replayed on restart. Slots missed after that run completes still qualify for catch-up. Forced runs leave their scheduled occurrence pending.
- When a running Gateway wakes after a deadline, the cron worker merges missed timer ticks using its stored deadlines and run requests. Scheduling, queue capacity, and durable results belong to the worker; the Gateway launches committed requests and joins the worker at shutdown.
- If
openclaw agentis driven from system cron or another external scheduler, wrap it with a hard-kill escalation even though the CLI already handlesSIGTERM/SIGINT. Gateway-backed runs ask the Gateway to abort accepted runs;--localruns receive the same abort signal. On GNUtimeout, prefertimeout -k 60 600 openclaw agent ...to plaintimeout 600 ...: the-kvalue is the backstop when the process cannot drain in time. For systemd units, use aSIGTERMstop signal with a grace window (TimeoutStopSec) before the final kill. Reusing a--run-idwhile the original Gateway run is still active reports the duplicate as in-flight rather than starting a second run.
Isolated run hardening
- Scheduled runs get their own execution lifetime, from admission through result persistence and cleanup. Timer ticks, startup catch-up, and event-source watchers do not inherit the request state of the conversation that created or updated them. Manual runs keep their caller authority.
- On completion, isolated runs make a best-effort attempt to close tracked browser tabs/processes for their
cron:<jobId>session, and they dispose of any bundled MCP runtime instances created for the job through the same shared teardown path that main-session and custom-session runs use. Cleanup failures are ignored so the run result still wins. - A run's continuation session remains available while descendant subagents are running or their completion delivery is pending, retention sweeps included, so cleanup cannot strand their results.
- Isolated runs holding the narrow automation self-cleanup grant can read scheduler status, a self-filtered list containing only their own job, and that job's run history, and they may remove only their own job.
- Isolated runs guard against stale acknowledgement replies: when the first result is only an interim status update (
on it,pulling everything together, and similar hints) and no descendant subagent still owes the final answer, OpenClaw re-prompts once for the actual result before delivery. Run totals count both completed prompts, priced by their respective models; session context usage still reflects the final model request. - Structured execution-denial metadata (including node-host
UNAVAILABLEwrappers whose nested error begins withSYSTEM_RUN_DENIEDorINVALID_REQUEST) is recognized, so a blocked command is not reported as a green run, and ordinary assistant prose is not mistaken for a denial. - Run-level agent failures count as job errors even when no reply payload exists, so model/provider failures increment error counters and trigger failure notifications instead of clearing the job as successful.
- When a job reaches
timeoutSeconds, the scheduler aborts the run and allows a short cleanup window. If it does not drain, Gateway-owned cleanup force-clears that run's session ownership before the scheduler records the timeout, so queued chat work is not stuck behind a stale processing session. - Setup/startup stalls get a phase-specific timeout (for example
cron: isolated agent setup timed out before runner startorcron: isolated agent run stalled before execution start (last phase: context-engine)). These watchdogs cover embedded and CLI-backed providers even before their external CLI process starts, and they are capped independently of longtimeoutSecondsvalues so cold-start/auth/context failures surface quickly.
Run reconciliation
Cron owns recovery and history for its own runs; an empty client process does not prove that a Gateway run has stopped.
The cron worker records execution and primary-delivery outcomes along with job state and history. Post-run notifications follow afterward; a notification failure cannot undo the recorded result.
Restart recovery matches a recorded terminal result to the exact run identity and restores it without running the payload or delivery again. Absent a recorded terminal result, an active run becomes interrupted with delivery Unknown. An enabled, unchanged, time-scheduled agent-turn job interrupted before delivery gets one fresh retry, one-shot jobs included. The retry restores current job authority and asks the agent to reconcile prior work before continuing; arbitrary interrupted tool effects are not guaranteed exactly once. Possible prior delivery, command/script payloads, and event or process-exit schedules are not replayed automatically. Inspect their history and recipient before retrying by hand. An early manual run preserves its separate scheduled occurrence even if it waited past that deadline before starting. The interrupted manual run still fails and is not retried. A separately rescheduled one-shot keeps its new occurrence.
Recurring restart retries keep the existing error-backoff floor, and their saved next-run time includes that delay. The interruption does not increase the failure streak. One-shot restart retries keep their short refire delay.
Queued timed requests can resume after a restart. Queued manual or event requests whose transient payload was lost record a skipped outcome rather than reconstructing the payload. Catch-up limits and delays still pace overdue scheduling. A terminal result with deleteAfterRun deletes the job only when completion is succeeded.
If a completed run's history was saved but its job update failed, a later acknowledged schedule or pacing edit keeps its next check through recovery. Recovery retains the completed history and settles the old run without executing its payload again. Saving an unchanged schedule does not change how that completed run is recovered.
Runs that get interrupted show up as failures in the run history. If the same interruption is recovered again, no additional history entry is created.
Promoting a repeated job into an automation
Most automations ought to begin as work the agent has already performed. Once you have requested roughly the same job a number of times, the agent proposes converting it into a schedule rather than just executing it one more time. Promoting existing work beats building a job from scratch, since the proposal carries over a run you have already reviewed: you know what the output looks like before it starts arriving on a schedule.
No repetition-detection engine exists, and no new history is stored. The agent spots the repeat from the conversation itself and checks automations(action: "list") for an existing job before proposing a new one, so a routine you already set up is not duplicated. When the automations tool is available, the prompt carries this guidance and instructs the agent to offer promotion only if its current actions include list, add, run, and remove. This workflow is not supported by read-only recovery or other restricted tool variants. This is model guidance; tool policy and approvals still govern the actions.
Before anything is created, the confirmation restates the schedule and the task in plain language, for example: "Every weekday at 07:00 Europe/Vienna, I summarize overnight updates and post them here." Confirm that sentence, not a cron expression.
Once you confirm, the agent:
- Creates the job, with delivery defaulting to the conversation where you asked: its channel and thread, or a conversation commit when there is no external route.
- Runs it once right away with
runinforcemode as a visible test, delivered to that same conversation, so you see real output well before the first scheduled occurrence. - Removes the job and tells you if that test fails.
The job is created enabled, not disabled-pending-approval, and that is a deliberate safety choice. Enabled jobs are supervised by the scheduler: a failing one raises a failure notification and is auto-disabled after repeated errors, with the reason recorded and the owner notified. A disabled job has no supervision at all. Left disabled while waiting for a confirmation that never comes, a job is invisible to every guard, hidden from the default automations list, and will never fire or explain itself, a silent non-outcome, which is a worse failure than a job that runs and visibly complains.
Creation is still gated by your confirmation, so nothing gets scheduled behind your back, and the test run is a real run with real delivery rather than a rendered preview: what you approve is exactly what the schedule will produce.