openclaw - 💡(How to fix) Fix Cron sessions stuck in status=running after completion [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#56121Fetched 2026-04-08 01:44:42
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
cross-referenced ×1

Error Message

  • The cron run log (cron/runs/*.jsonl) correctly records finished + ok/error
  • 86% of error runs lose their sessionId (not recorded before execution fails)

Root Cause

The cron execution path (executeJobCore) manages the run log and job-level state correctly. However, session entry updates rely on the subagent registry (resolveSubagentSessionStatus), which does not fire for cron-spawned isolated sessions because they are not registered as subagent runs.

Two independent state machines are out of sync:

  • Cron run log: trigger → execute → finished ✅
  • Session entry: create → running → (never updated) ❌

Fix Action

Workaround

A cleanup script that periodically removes stale :cron:/:run: entries from sessions.json where status=running and updatedAt is older than 24h.

RAW_BUFFERClick to expand / collapse

Bug Description

Cron sessions remain in status=running with endedAt=NONE in sessions.json after the cron run has completed successfully (run log shows finished/ok).

Evidence

  • 17 sessions stuck in status=running, oldest 19.9 hours
  • The cron run log (cron/runs/*.jsonl) correctly records finished + ok/error
  • But sessions.json entries never transition from runningdone/failed
  • status=done was never observed across 45+ cron session entries
  • Only status=failed entries (5 out of 45) have endedAt correctly set

Root Cause Analysis

The cron execution path (executeJobCore) manages the run log and job-level state correctly. However, session entry updates rely on the subagent registry (resolveSubagentSessionStatus), which does not fire for cron-spawned isolated sessions because they are not registered as subagent runs.

Two independent state machines are out of sync:

  • Cron run log: trigger → execute → finished ✅
  • Session entry: create → running → (never updated) ❌

Additional Issues

  • 86% of error runs lose their sessionId (not recorded before execution fails)
  • sessionRetention only cleans transcript files, not session entries — entries accumulate without bound
  • :run: sub-entries in sessions.json are never cleaned

Expected Behavior

When a cron job finishes:

  1. Session entry status should be set to done/failed/timeout
  2. endedAt should be set to the completion timestamp
  3. This should happen for ALL cron sessions, not just subagent-registered ones

Environment

  • OpenClaw version: latest (as of 2026-03-28)
  • macOS, local gateway mode
  • 16 active cron jobs

Workaround

A cleanup script that periodically removes stale :cron:/:run: entries from sessions.json where status=running and updatedAt is older than 24h.

extent analysis

Fix Plan

To address the issue of cron sessions remaining in status=running after completion, we need to update the session entry status and endedAt timestamp. Here are the steps:

  • Update the executeJobCore function to directly update the session entry status and endedAt timestamp when a cron job finishes.
  • Modify the resolveSubagentSessionStatus function to also handle cron-spawned isolated sessions.
  • Implement a periodic cleanup task to remove stale session entries.

Example Code

# Update executeJobCore function
def executeJobCore(job):
    # ... existing code ...
    if job['status'] == 'finished':
        update_session_entry(job['sessionId'], 'done', datetime.now())

# Update resolveSubagentSessionStatus function
def resolveSubagentSessionStatus(session_id):
    # ... existing code ...
    if session_id.startswith(':cron:'):
        # Handle cron-spawned isolated sessions
        update_session_entry(session_id, 'done', datetime.now())

# Implement periodic cleanup task
def cleanup_stale_sessions():
    with open('sessions.json', 'r+') as f:
        sessions = json.load(f)
        for session in sessions:
            if session['status'] == 'running' and session['updatedAt'] < datetime.now() - timedelta(hours=24):
                session['status'] = 'done'
                session['endedAt'] = datetime.now()
        f.seek(0)
        json.dump(sessions, f)
        f.truncate()

# Call cleanup task periodically (e.g., every hour)
schedule.every(1).hours.do(cleanup_stale_sessions)

Verification

To verify that the fix worked, check the sessions.json file for updated session entries with status=done and endedAt timestamps. Also, monitor the cron job logs to ensure that the session entry status is updated correctly after each job completion.

Extra Tips

  • Make sure to handle errors and exceptions properly when updating session entries.
  • Consider implementing a retry mechanism for failed updates to ensure data consistency.
  • Review the sessionRetention policy to ensure it correctly cleans up session entries and transcript files.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING