Articles by Victoria

Building My Second Brain on OpenClaw (Part 10)

#AI#OpenClaw#Automation#Debugging#System Design
Aug 24, 202613 min read
cover

Welcome back to another Articles by Victoria, the place where I randomly write things I'm curious about.

Part 10 is about something I did not expect to become a problem. My cron jobs, which had been running reliably for months, started timing out. Not all at once. Not dramatically. Just gradually, like a car that takes longer to start each morning until one day it does not start at all.

The daily Todoist sync, which used to finish in under a minute, started hitting 3-minute timeouts. Then 5-minute timeouts. Then it failed entirely. The fix turned out to be one configuration change, but understanding why that change mattered required digging into how OpenClaw actually manages sessions under the hood.

Here's the parts in case you need to catch up:

This post is about what I learned: how sessions grow, why that growth breaks cron jobs, and what a more robust design would look like.

The Symptom: A Job That Used to Work

The daily-todoist-sync job has been running at 3 PM SGT every day since I built it. Its job is simple: fetch my active tasks from Todoist, check which ones have due dates, and create 15-minute reminder cron jobs for any timed events.

Here is what the run history looked like over two weeks:

Date Duration Result
May 14 300s Timeout ❌
May 13 180s Timeout ❌
May 12 73s Success ✅
May 11 47s Success ✅
May 10 146s Success ✅
May 9 155s Success ✅
May 7 120s Timeout ❌
Apr 30 120s Timeout ❌

The pattern is clear. When the job succeeds, it takes 47 to 155 seconds. When it times out, it hits the limit even at 300 seconds. The job is not doing more work on the slow days. The Todoist task list is roughly the same size. Something else is getting slower.

The Root Cause: Session Bloat

The job was originally configured with sessionTarget: "session:agent:main:telegram:group:-1003828113148:topic:314". This means it runs in the main session, the same session that handles my Telegram messages, rather than in an isolated session.

This seemed fine at first. The job needs to create reminder cron jobs, and those reminders deliver to Topic 314. Running in the main session means the job inherits the delivery context automatically.

The problem is that the main session never gets smaller. Every message I send, every tool call the agent makes, every response it generates, all of that gets appended to the session transcript. Over days and weeks, the session accumulates thousands of turns.

When the cron job fires, OpenClaw loads the entire session history into context before executing the job. The agent has to process all of that accumulated conversation, even though none of it is relevant to fetching Todoist tasks. The context window gets consumed by old chat history, leaving less room for the actual work. The model slows down because it is parsing irrelevant conversation. Eventually, the job exceeds the timeout before it can finish.

This is not a bug in the cron scheduler. It is a consequence of how session persistence works when you bind a recurring job to a long-lived session.

How OpenClaw Stores Sessions

To understand why this happens, I had to look at how OpenClaw actually manages session state.

Where State Lives

All session data is owned by the Gateway process. According to the OpenClaw documentation on session management, sessions are stored in two places:

  • ~/.openclaw/agents/<agentId>/sessions/sessions.json — the session registry with lifecycle metadata
  • ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl — the full transcript of every turn in that session

The sessions.json file tracks when the session started, when the last interaction happened, and when it was last updated. The .jsonl transcript file is the one that grows over time. Every message, every tool call, every system event gets appended as a new line.

Session Lifecycle

Sessions do not last forever, but they last longer than you might expect. By default, OpenClaw resets sessions daily at 4:00 AM local time. There is also an optional idle reset that creates a new session after a period of inactivity. But here is the key detail from the docs: heartbeat, cron, exec, and other system-event turns may write session metadata, but those writes do not extend daily or idle reset freshness.

This means a cron job running in the main session does not keep the session alive in a meaningful way, but it also does not prevent the session from growing. The session accumulates history until the daily reset rolls it over.

Session Maintenance and Pruning

OpenClaw has a maintenance system that can prune old sessions. The default mode is warn, which reports what would be cleaned but does not actually clean it. You can set it to enforce for automatic cleanup with pruneAfter and maxEntries limits.

But maintenance preserves durable external conversation pointers, including group sessions and thread-scoped chat sessions. A cron job bound to a group chat session is effectively attached to a durable session that maintenance will not clean up aggressively.

The Four Session Targets for Cron

OpenClaw cron jobs support four execution styles, and the choice of session target determines whether you hit this bloat problem:

Style Value Runs In Best For
Main session main Next heartbeat turn Reminders, system events
Isolated isolated Dedicated cron:<jobId> session Reports, background chores
Current session current Bound at creation time Context-aware recurring work
Custom session session:custom-id Persistent named session Workflows that build on history

The docs explicitly recommend isolated for reports and background chores. I had configured my job as a custom session bound to the main Telegram group, which behaves like the current style: it inherits all the context from the bound session.

Why This Design Exists

The custom session and current session targets exist for good reasons. If you want a daily standup summary that builds on yesterday's summary, you need a persistent session that remembers what was discussed. If you want a weekly report that references last week's findings, you need history.

The tradeoff is that persistent sessions accumulate state, and that state has costs:

  1. Context window consumption — every turn in the transcript eats into the model's available context window
  2. Processing overhead — the model spends tokens parsing irrelevant history before getting to the actual task
  3. Timeout risk — slower processing means jobs are more likely to hit timeout limits
  4. Cost inflation — more context means more tokens per request, which means higher API costs

For jobs that do not need historical context, like fetching today's tasks or checking calendar events, isolated sessions are the correct choice. The job gets a clean slate every time.

The Fix: One Configuration Change

The fix for my daily-todoist-sync job was to change sessionTarget from the bound main session to isolated. This gives the job a fresh cron:<jobId> session on every run, with no inherited history.

The timeout remains at 300 seconds, which is more than enough for the actual work. Fetching tasks from Todoist takes a few seconds. Creating reminder cron jobs takes a few more. The job now consistently completes in under 30 seconds.

The job still delivers to Topic 314 because the delivery target is explicitly configured in the job definition. The session target controls where the job runs, not where its output goes. Those are separate concerns, and I had conflated them.

Other Failure Modes I Found

While investigating this, I came across several GitHub issues and Reddit threads describing related cron problems. These are worth knowing about because they show how session management affects reliability in different ways.

The 600-Second Hard Timeout

GitHub issue #27427 describes isolated cron jobs that always log status: "error" at exactly 600 seconds, even when the job actually completed successfully. The orchestrator session continues running, receives all sub-agent results, and delivers the output correctly. But the cron runtime marks it as timed out because a 600-second threshold appears to be hardcoded regardless of timeoutSeconds configuration.

This is a different bug, but it compounds the session bloat problem. If your job is already slow because of bloated context, and then it gets killed at 600 seconds even if it would have finished, you have two problems stacked on top of each other.

Provider Timeouts in Isolated Sessions

GitHub issue #49597 describes OpenRouter provider timeouts that only happen in cron isolated sessions. The same model works fine in direct chat and manual subagent spawns, but consistently times out at ~125 seconds when run from cron with sessionTarget: "isolated".

The workaround is to use a local model for cron jobs, but the underlying issue suggests that isolated sessions may have different network or authentication behavior than main sessions.

Silent Failures on Telegram

A Reddit thread from February 2026 describes cron jobs that execute successfully in logs but never send Telegram messages. The fix involves ensuring the session is isolated and explicitly defining the delivery target in the payload.

All of these issues share a common theme: cron job reliability depends heavily on session configuration, and the interaction between session targets, timeouts, and provider behavior is not always intuitive.

Designing a More Robust System

Based on what I learned, here is how I would design a more robust cron session system if I were building it from scratch.

Principle 1: Default to Isolated

The default session target for new cron jobs should be isolated, not main or current. Most cron jobs do not need historical context. They need to run a task and deliver output. Defaulting to isolated would prevent the most common cause of session bloat failures.

If a user explicitly wants a persistent session for a workflow that builds on history, they should opt into it with session:custom-id. That opt-in should come with a warning about the accumulation tradeoff.

Principle 2: Context Budgets, Not Just Timeouts

Timeouts are necessary but insufficient. A job should also have a context budget: a maximum number of tokens or transcript turns that the session can consume before the job starts. If the session exceeds that budget, the job should either fail fast with a clear error or automatically switch to a clean session.

This would have caught my Todoist sync problem before it started timing out. The job would have failed with "session context exceeds budget" instead of mysteriously slowing down over weeks.

Principle 3: Session Health Metrics

The cron runtime should track and expose session health metrics:

  • Session transcript size before job start
  • Context window utilization percentage
  • Average processing time per turn
  • Historical trend of job duration

With these metrics, I could have seen that my job was getting slower because the session was growing, not because the Todoist API was slower.

Principle 4: Automatic Session Rotation for Custom Sessions

For jobs that genuinely need persistent sessions, the system should support automatic rotation. Instead of one session that grows forever, the job could maintain a sliding window of recent history. Old turns get summarized or pruned, and the session stays bounded.

OpenClaw already has session compaction and pruning features, but they are manual or time-based. A context-aware compaction that triggers when a session-bound cron job is about to run would be more useful.

Principle 5: Clearer Error Messages

When a cron job times out, the error message should include session context. Instead of just "timeout at 300s", it should say "timeout at 300s after processing 4,200 transcript turns (12,800 tokens of context)". This would immediately point users toward the session bloat diagnosis.

Principle 6: Pre-Flight Checks

Before executing a cron job, the scheduler should run a pre-flight check:

  1. Is the session target valid?
  2. If bound to an existing session, how large is its transcript?
  3. Does the estimated context fit within the job's timeout budget?
  4. If not, warn or automatically switch to isolated.

This would be a small amount of overhead that prevents a large class of failures.

What I Changed in My Setup

Beyond fixing the daily-todoist-sync job, I audited all my other cron jobs:

  • Daily Event Scan (daily-calendar-sync): Already using isolated
  • ragTech Collab Evaluator (ragtech-collab-eval): Already using isolated
  • Daily 9 AM Japanese Lesson: Already using isolated
  • Daily 9 PM Workout Prompt: Already using isolated
  • Daily 10 PM Mood Summary: Already using isolated
  • Weekly Review (Sunday 8 PM): Already using isolated

The Todoist sync was the only job bound to the main session. I had set it up that way because I thought it needed the delivery context, but I now understand that delivery targets are independent of session targets.

I also added a note to my cron job creation checklist: always start with sessionTarget: "isolated", and only use a custom session if the job explicitly needs to reference previous runs.

Sources and References

Conclusion: Prevention is better than cure

Psyduck from Pokémon, looking confused and overwhelmed — the universal feeling after debugging session bloat

Psyduck representing the universal feeling after a successful debugging session

The lesson here is that state accumulates whether you think about it or not. A session that works fine today will be slower next week and broken next month if it keeps growing. The fix is usually simple once you understand the mechanism, but the mechanism is not obvious from the outside.

OpenClaw's session system is powerful. Custom sessions enable workflows that build on history, which is genuinely useful for things like weekly reviews or adaptive learning systems. But that power comes with a cost: sessions grow, context windows shrink, and jobs that used to work quietly start failing.

The robust design is to default to isolation, measure what matters, and let users opt into persistence with full knowledge of the tradeoff.

Thanks for reading! I am curious to know your own personal thoughts and experiences on this topic! Feel free to connect, send me an email (my inbox is always open) or let me know in the comments! Cheers!

Let's Connect!

Share:

More from Building My Second Brain on OpenClaw

View full series →

Further Reading