DEV Community

Cover image for Why Your OpenClaw Cron Jobs Should Run in Isolation
Octomind.dev
Octomind.dev

Posted on • Originally published at octoclaw.ai

Why Your OpenClaw Cron Jobs Should Run in Isolation

Why Your OpenClaw Cron Jobs Should Run in Isolation

Category: Engineering

Slug: isolated-cron-jobs-reliability

Read time: ~12 min

Image key: isolated-cron-jobs


Most people set up their first OpenClaw cron job in the simplest way possible: attach it to the main session, let it share context with everything else, and move on. It works — until it doesn't. Then it fails in ways that are hard to debug, hard to predict, and occasionally embarrassing when garbled output lands in a Slack channel or Telegram message at 7 AM.

There is a better way. OpenClaw's isolated cron execution model addresses the reliability problems that come with shared-session scheduling, and the engineering principles behind why it works are well-established, well-documented, and not specific to AI agents at all. This post walks through the difference between the two modes, the concrete failure modes that isolation prevents, and how to choose the right approach for every job you schedule.


OpenClaw's Cron System in 60 Seconds

Before getting into reliability, a quick orientation on how OpenClaw's scheduler works.

Cron runs inside the Gateway — the persistent daemon that keeps OpenClaw alive between conversations. Jobs are stored under ~/.openclaw/cron/jobs.json, which means they survive restarts and reboots. The scheduler supports three types of schedules:

  • --at for one-shot execution at a specific timestamp
  • --every for interval-based repetition ("every 6 hours")
  • --cron for Unix-style cron expressions ("every weekday at 8 AM")

You can schedule anything: a morning news summary, a weekly project review, a reminder in 20 minutes. The question is not what to schedule but how the execution should happen — and that comes down to the session mode.


The Two Modes: Main Session vs. Isolated

When you schedule a cron job in OpenClaw, you make a fundamental architectural choice: where does the job actually execute?

The official docs describe it cleanly:

Main session: enqueue a system event, then run on the next heartbeat.

Isolated: run a dedicated agent turn in cron:<jobId>, with delivery by default.

In practice:

  • --session main injects the job's prompt as a system event into your existing main agent session. Whatever conversation history, tool outputs, and accumulated context is sitting in that session gets loaded alongside the job. The job does not start fresh — it inherits everything.

  • --session isolated spins up a brand new session for that job, with its own sessionId and a clean transcript. It starts from scratch, executes its task, and optionally delivers output directly to a channel — without touching the main session at all.

The difference sounds subtle. The reliability implications are anything but.


The Failure Modes of Main-Session Scheduling

1. Context Compaction Degrades Output Quality

Large language models have a finite context window. When a long-running main session approaches that limit, OpenClaw triggers context compaction — a process that summarizes older conversation turns to free up space. The summary keeps recent turns intact but condenses older ones.

This is fine for normal conversation. It is a reliability hazard for scheduled jobs.

GitHub issue #2965, filed in January 2026, documents the problem directly:

"When the main agent session undergoes context compaction (hitting token limits), cron jobs can produce degraded or nonsensical output that gets delivered to end users."

The mechanics are straightforward. A main-session cron job fires. The agent loads its full session context. If compaction produced a degraded summary — the issue notes "Summary unavailable due to context limits" as a real example — the agent loses awareness of the job's intent. The cron payload is injected, but without useful context to act on it, the output is garbage. And because main-session jobs run inside the same turn loop, that garbage gets delivered.

Isolated jobs are unaffected. They start with a clean session and load only what they need.

2. Token Costs Spiral Out of Control

Even without hitting the compaction cliff, main-session cron jobs burn tokens needlessly.

GitHub issue #1594 describes the mechanism:

"Main session cron jobs enqueue a system event into the main heartbeat loop → full main session context is loaded (including any prior huge tool dumps or 1000-message history) → same risk of context explosion if the job triggers large tool outputs or chains. Isolated session cron jobs (the recommended mode for most scheduled tasks) largely avoid the problem."

If your main session has been running for days — long conversation history, large file reads, tool outputs from previous tasks — every main-session cron job drags all of that forward. A simple "summarize overnight news" job does not need your three-day conversation history. With isolated execution, it does not get it.

For high-frequency jobs this adds up fast. The token cost of a clean isolated session is bounded by the job itself. The token cost of a main-session job is bounded by everything that has ever happened in your session.

3. Model Overrides Affect the Wrong Thing

One of the more powerful features of isolated cron jobs is the ability to specify a model and thinking level per job. A weekly deep analysis might warrant --model opus --thinking high. A quick status ping does not.

The OpenClaw docs note a critical caveat:

"You can set model on main-session jobs too, but it changes the shared main session model. We recommend model overrides only for isolated jobs to avoid unexpected context shifts."

Changing the model on a main-session job is a side effect that outlasts the job. If your morning briefing runs at 7 AM and switches the main session to a heavier model, every interaction for the rest of the morning uses that model — your own messages, unrelated tasks, other heartbeat checks. The briefing job is long done, but its footprint remains. Isolated jobs have no such contamination risk. The model choice lives and dies with the session.

4. Errors Leak to Messaging Surfaces

This one is embarrassing. GitHub issue #2654 documents that cron isolation internal errors — gateway timeouts, execution failures — can leak directly to messaging surfaces via postToMain.

When a main-session job fails mid-execution, its error state is part of the session transcript. The session may attempt to deliver whatever it has produced. Users get raw error JSON or truncated output in their Slack or Telegram. This is the kind of failure mode that erodes trust fast — automated messages are only useful if users can rely on them to be coherent.

Isolated jobs with deliver: true send to a channel only upon completion. If a job times out or errors, the failure is contained within the job's own session. The main session continues running normally; no garbage gets pushed downstream.

5. Deadlocks and Scheduling Conflicts

GitHub issue #1812 tracks a "Deadlock between cron timer lock and agent tool calls." The problem arises when a cron job fires while the main session is in the middle of an active tool call chain. The scheduler and the agent compete for the same session lock.

With isolated execution, the cron job runs in its own session. There is no shared lock to contend for. The main session continues its work; the cron job runs concurrently without interference. This is especially relevant for users who run complex, multi-tool workflows in the main session — the scheduler firing at an inconvenient moment should never block or corrupt what is already in progress.

6. Debugging Is Near-Impossible

There is a sixth problem, more operational than technical: when a main-session cron job produces bad output, diagnosing why is painful. The cron job's execution is interwoven with the main session history. Was it context compaction? A model that was left in the wrong state? A tool call that hit the lock? The signals are mixed together.

GitHub issue #27427 documents the debugging gap directly:

"Debugging via sessions_history on the cron session key returns: { 'status': 'forbidden', 'error': 'Session history visibility is restricted to the current session tree.' } — This makes post-mortem debugging of cron jobs impossible from within the agent itself."

Isolated cron jobs have their own sessionId. When something goes wrong, you can inspect that session in isolation, without wading through the noise of the main session history.


Why Isolation Works: The Engineering Principle

None of this is specific to AI agents. The reliability case for process isolation is one of the oldest lessons in systems engineering.

The Google SRE Book's chapter on distributed periodic scheduling frames the core principle around failure domains:

"Cron's failure domain is essentially just one machine. If the machine is not running, neither the cron scheduler nor the jobs it launches can run."

The point is that a failure domain defines the blast radius of any single failure. On a single machine, everything shares the same failure domain — if the machine goes down, all jobs go down together. In distributed systems, you introduce smaller, isolated failure domains to limit how far any single failure propagates. The entire practice of microservices, containers, and serverless functions is built on this premise.

The same logic applies to OpenClaw sessions. A main-session cron job shares its failure domain with your entire interactive session. Context compaction? Your job degrades. Model swap? Your job's output changes unexpectedly. Active tool call chain? Your job might deadlock. The main session is a shared resource, and shared resources are where reliability goes to die.

An isolated cron job creates its own failure domain. It can fail, produce garbage, or time out — and your main session keeps running, completely unaffected. The blast radius is exactly one job.

This is the same principle behind the Noisy Neighbor antipattern documented by Microsoft's Azure Architecture Center. When workloads share resources without isolation, they create unpredictable interference. The solution is always the same: isolate the workloads.


The Practical Rule

A good rule of thumb, derived from both the OpenClaw documentation and the failure modes above:

Use --session isolated for:

  • Recurring jobs that produce output (morning briefings, summaries, weekly reports)
  • Any job that delivers to a channel or sends a notification
  • Jobs with model or thinking-level overrides
  • Long-running jobs or anything that chains multiple tool calls
  • Jobs that run more than a few times per day

Use --session main for:

  • Simple reminders that inject a note into your current conversational context
  • Jobs where continuity with the current conversation genuinely matters
  • One-shot --at reminders tied to something happening right now in your workflow

If you are unsure, default to isolated. The overhead is negligible. The reliability gain is real.


What This Looks Like in Practice

Here is a typical morning briefing job, set up the right way:

openclaw cron add \
  --name "Morning brief" \
  --cron "0 7 * * *" \
  --tz "Europe/Berlin" \
  --session isolated \
  --message "Check emails, calendar for today, and any GitHub notifications. Summarize the top 3 priorities." \
  --announce \
  --channel slack \
  --to "channel:C1234567890"
Enter fullscreen mode Exit fullscreen mode

This fires at 7 AM Berlin time, creates a clean session, runs the task, and delivers output directly to Slack. If the job fails, nothing leaks to the main session. If your main session has accumulated a 2,000-message history from yesterday, the briefing does not pay for it in tokens.

For a weekly deep-analysis job where you want a more capable model:

openclaw cron add \
  --name "Weekly project analysis" \
  --cron "0 9 * * 1" \
  --tz "Europe/Berlin" \
  --session isolated \
  --message "Review this week's git commits, open issues, and project notes. Identify blockers and the top 3 risks going into next week." \
  --model "opus" \
  --thinking high \
  --announce \
  --channel slack \
  --to "channel:C1234567890"
Enter fullscreen mode Exit fullscreen mode

Running this as a main-session job with --model opus --thinking high would switch your entire interactive session to Opus until something resets it. Isolated execution contains the model choice to exactly this job.

Contrast with a simple one-shot reminder where main-session is fine:

openclaw cron add \
  --name "PR review reminder" \
  --at "2026-03-15T14:00:00Z" \
  --session main \
  --system-event "Reminder: review the open PRs on the octoclaw repo before end of day." \
  --wake now \
  --delete-after-run
Enter fullscreen mode Exit fullscreen mode

This is a one-shot nudge. It does not deliver to an external channel. It does not need a model override. It benefits from main-session context because you are already working on that repo. This is the right use case for --session main.


Auditing and Migrating Your Existing Jobs

If you have been running OpenClaw for a while, there is a good chance some of your jobs are set to --session main by default — either because that was the easier option at setup time, or because isolated execution was added or clarified in a later version.

Auditing is straightforward:

openclaw cron list
Enter fullscreen mode Exit fullscreen mode

This shows all scheduled jobs with their current configuration. Look for sessionTarget: "main" entries that have delivery.mode: "announce" or any external channel in delivery.to. These are your risk candidates — jobs that run in the shared session but push output to external surfaces.

Migrating one is also simple. Delete the old job and recreate it with --session isolated:

# Remove the old main-session job
openclaw cron remove --id <job-id>

# Recreate it as isolated
openclaw cron add \
  --name "Morning brief" \
  --cron "0 7 * * *" \
  --tz "Europe/Berlin" \
  --session isolated \
  --message "Check emails, calendar, and notifications. Summarize the top 3 priorities." \
  --announce \
  --channel slack \
  --to "channel:C1234567890"
Enter fullscreen mode Exit fullscreen mode

There is one exception worth checking: if a main-session job does not have delivery configured and only injects a system event into your workflow, it may be intentional. A reminder that asks "did you follow up on X?" might legitimately benefit from main-session context. Leave those alone. Target the ones delivering to external channels.


One Nuance: Heartbeats Are Different

Heartbeats are the one recurring case where main-session execution is often the right call. Heartbeats are designed to batch multiple lightweight checks into a single turn — checking email, calendar, and notifications together, with access to recent conversational context.

The OpenClaw documentation is explicit about the trade-off: if you need conversational context from recent messages, heartbeats in the main session make sense. If timing can drift slightly and the checks are lightweight, the simplicity of main-session heartbeats is worth it.

The key distinction is output with delivery. Heartbeats that simply check things and inject notes are low-risk in the main session — they are essentially part of the conversation. The moment a job is expected to deliver something to an external channel — a report, a summary, a notification — isolation becomes non-negotiable. That is when all the failure modes above become actual user-facing problems.


The Bottom Line

Running cron jobs in the main session is the easy default. It requires less thought and usually works fine for the first few jobs. As automation grows — more jobs, higher frequency, longer session history — the failure modes compound: context compaction degrades output, token costs balloon, model overrides leak across tasks, errors surface in places they should not.

Isolated cron execution is not a workaround or an advanced feature. It is the architecturally correct default for any job that produces and delivers output. The OpenClaw docs recommend it explicitly. The GitHub issue tracker documents what real-world failures look like when it is skipped. The engineering principle is the same one Google's SRE teams apply to distributed scheduling: minimize failure domains, and the blast radius of any single failure stays bounded.

If you are setting up recurring jobs on OpenClaw, start with --session isolated. Save the main session for the cases where shared context genuinely adds value — and even then, keep an eye on whether that context is helping or getting in the way.


Want to run OpenClaw without the setup headache? OctoClaw gives you a fully hosted instance in minutes — pre-configured, pre-provisioned, and ready to automate from day one.


Sources


This article was originally published on OctoClaw. OctoClaw provides turnkey cloud-hosted OpenClaw instances — up and running in minutes, no self-hosting pain.

Top comments (0)