Cron Semaphore

Serializes cron job execution in OpenClaw to prevent concurrent runs and session contention

Install

openclaw plugins install clawhub:cron-semaphore

Cron Semaphore — OpenClaw Plugin

Prevents concurrent cron job execution in OpenClaw. When two cron jobs are scheduled at the same time, one runs and the others wait their turn — no more session corruption errors from jobs stepping on each other.

Why

OpenClaw fires each cron job independently. If two jobs land on the same minute (e.g. 0 18 * * 0), both spawn isolated agent runs simultaneously. Those runs share the same agent runtime, and their embedded sessions can collide — producing EmbeddedAttemptSessionTakeoverError or mysterious timeouts as they fight over session files.

This plugin serializes those runs. Only one cron job runs at a time. The rest are cleanly skipped with a message and retry on their next scheduled interval.

How it works

  1. A cron job starts → before_agent_run hook fires.
  2. Plugin checks a lock file in the workspace directory.
  3. No lock? Acquire it and let the job through.
  4. Lock exists from another job? Block this run with a clean message.
  5. Job finishes → agent_end hook fires → lock is released.
  6. Stale locks (crash recovery) are auto-cleared after 10 minutes or on Gateway restart.

Install

openclaw plugins install clawhub:cron-semaphore

Or from the repo:

git clone https://github.com/GMDEEP/cron-semaphore.git
cd cron-semaphore
openclaw plugins install --link .

Configure

The plugin activates on startup. For it to intercept conversation hooks, you must enable conversation access in your OpenClaw config:

{
  plugins: {
    entries: {
      "cron-semaphore": {
        config: {
          // (optional) Override stale lock timeout in ms. Default: 600000 (10 min)
          staleTimeoutMs: 600000
        },
        hooks: {
          // Required — the before_agent_run hook is a conversation hook
          allowConversationAccess: true
        }
      }
    }
  }
}

Config options

OptionTypeDefaultDescription
staleTimeoutMsinteger600000Milliseconds before a lock is considered stale
lockFilePathstringautoCustom path for lock file (defaults to workspace)

Lock file

The plugin writes .cron-semaphore-lock.json in the workspace directory. It contains:

{
  "jobId": "a6914b45-b21c-4e5e-87d0-63fa976a04ed",
  "jobName": "a6914b45...",
  "acquiredAt": 1780883000000,
  "lastHeartbeat": 1780883005000
}

If a job crashes without releasing the lock, it auto-expires after the stale timeout (10 minutes by default). Gateway restart also cleans stale locks.

License

MIT