openclaw - ✅(Solved) Fix Cron store: `jobId` in jobs.json not normalized to `id`, causes "unknown cron job id" [1 pull requests, 1 comments, 2 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#62246Fetched 2026-04-08 03:07:16
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Timeline (top)
closed ×1commented ×1cross-referenced ×1

When cron jobs are stored in ~/.openclaw/cron/jobs.json with the jobId field (consistent with docs that describe jobId as the canonical identifier for Gateway/API tool calls), the scheduler fails to resolve those jobs at runtime.

Error Message

Cron failed: Error: unknown cron job id: <stable-job-slug>

Root Cause

When cron jobs are stored in ~/.openclaw/cron/jobs.json with the jobId field (consistent with docs that describe jobId as the canonical identifier for Gateway/API tool calls), the scheduler fails to resolve those jobs at runtime.

Fix Action

Fix / Workaround

Happy to help test a patch or adjust the issue if maintainers prefer a specific direction (migration vs dual-key lookup vs doc-only).

PR fix notes

PR #62251: Cron: normalize jobId to id when loading jobs.json

Description (problem / solution / changelog)

Summary

  • Problem: Cron jobs stored with only jobId (no id) in jobs.json failed at runtime with unknown cron job id because the scheduler looked up jobs by id only. Doctor migration already fixed this on doctor --fix, but the gateway load path did not.
  • Why it matters: Users editing the store or following docs that emphasize jobId expect stable identifiers to work without running doctor first.
  • What changed: Shared normalizeCronJobIdentityFields runs when the cron service loads the store (and doctor migration reuses it) so id is set from jobId when needed and the legacy jobId key is dropped in memory.
  • What did NOT change: On-disk files are not rewritten on load alone; the next explicit persist still writes the canonical shape.

Change Type

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #62246
  • Related #62246
  • This PR fixes a bug or regression

Root Cause

  • Root cause: The on-disk JSON shape allowed jobId without id; runtime lookup used id only, while doctor migration normalized the file separately.
  • Missing detection / guardrail: Runtime load did not apply the same identity normalization as doctor.
  • Contributing context: API and tools often refer to the stable name as jobId while persisted jobs use id.

Regression Test Plan

  • Coverage level: Unit test
  • Target test: src/cron/normalize-job-identity.test.ts, src/cron/service/store.test.ts
  • Scenario: A job with only jobId in jobs.json loads and findJobOrThrow resolves the stable id; disk file unchanged until persist.

User-visible / Behavior Changes

  • Cron jobs in jobs.json that only define jobId are now recognized by the scheduler and cron operations that resolve jobs by id without requiring openclaw doctor --fix first.

Diagram

N/A

Security Impact

  • New permissions/capabilities? No
  • New network endpoints or listeners? No
  • Trust boundary changes? No

Made with Cursor

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/commands/doctor-cron-store-migration.ts (modified, +4/-11)
  • src/cron/normalize-job-identity.test.ts (added, +53/-0)
  • src/cron/normalize-job-identity.ts (added, +18/-0)
  • src/cron/service/store.test.ts (modified, +60/-0)
  • src/cron/service/store.ts (modified, +10/-0)

Code Example

Cron failed: Error: unknown cron job id: <stable-job-slug>
RAW_BUFFERClick to expand / collapse

Summary

When cron jobs are stored in ~/.openclaw/cron/jobs.json with the jobId field (consistent with docs that describe jobId as the canonical identifier for Gateway/API tool calls), the scheduler fails to resolve those jobs at runtime.

Observed error

Cron failed: Error: unknown cron job id: <stable-job-slug>

The stable slug matches the string you put in jobId, but the in-memory lookup uses job.id only.

Why it happens (current behavior)

  • findJobOrThrow matches with state.store.jobs.find((j) => j.id === id).
  • Jobs created via the normal cron.add path get an id (e.g. UUID).
  • Hand-edited or migrated JSON that only has jobId never gets an id, so lookups by the documented stable name fail.

Expected behavior (one of)

  1. Load-time migration: In ensureLoaded (or equivalent), if jobId is present and id is missing, set id from jobId (and optionally drop jobId on persist for a single canonical shape), or
  2. Lookup: Resolve jobs by id or jobId consistently, or
  3. Docs: Explicitly state that the on-disk store must use id (not jobId) and that jobId is only for RPC/tool payloads—if that is intentional.

Minimal reproduction idea

  1. Stop the gateway.
  2. Add a job object to jobs.json with "jobId": "repro-stable-id" and no "id", valid schedule, sessionTarget, payload, etc.
  3. Start the gateway; trigger a wake/run that references repro-stable-id.
  4. Observe unknown cron job id: repro-stable-id.

Environment

  • Observed on packaged OpenClaw (cron scheduler / gateway), version in the 2026.2.x line; likely applies to any build sharing the same store + lookup logic.

Happy to help test a patch or adjust the issue if maintainers prefer a specific direction (migration vs dual-key lookup vs doc-only).

extent analysis

TL;DR

To fix the cron job resolution issue, update the findJobOrThrow function to match jobs by either id or jobId.

Guidance

  • Verify that the jobId field in jobs.json matches the expected id format, as the current implementation only looks up jobs by id.
  • Consider implementing a load-time migration in ensureLoaded to set id from jobId if id is missing, ensuring consistency in the job store.
  • Alternatively, update the lookup logic to resolve jobs by both id and jobId to maintain backwards compatibility.
  • Test the fix using the provided minimal reproduction steps to ensure the issue is resolved.

Example

// Example load-time migration in ensureLoaded
if (job.jobId && !job.id) {
  job.id = job.jobId;
  // Optionally drop jobId on persist for a single canonical shape
  // delete job.jobId;
}

Notes

The fix may require adjustments to the documentation to reflect the expected format for id and jobId in the job store.

Recommendation

Apply a workaround by updating the findJobOrThrow function to match jobs by either id or jobId, as this approach maintains backwards compatibility and resolves the issue without requiring significant changes to the existing implementation.

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