Skip to content

fix(cron): guard against undefined job.state in all accesses#9289

Closed
fxd-jason wants to merge 1 commit intoopenclaw:mainfrom
fxd-jason:fix/cron-guard-all-job-state-accesses
Closed

fix(cron): guard against undefined job.state in all accesses#9289
fxd-jason wants to merge 1 commit intoopenclaw:mainfrom
fxd-jason:fix/cron-guard-all-job-state-accesses

Conversation

@fxd-jason
Copy link

@fxd-jason fxd-jason commented Feb 5, 2026

Systematically add optional chaining (?.) to all job.state accesses to prevent runtime errors when state object is missing or incomplete.

This is a comprehensive fix for a class of issues where job.state could be undefined during job creation, data migration, or edge cases.

Changes:

  • src/cli/cron-cli/shared.ts:

    • formatStatus: Guard job.state.runningAtMs and lastStatus
    • printCronList: Guard job.state.nextRunAtMs and lastRunAtMs
  • src/cron/service/jobs.ts:

    • computeJobNextRunAtMs: Guard job.state.lastStatus and lastRunAtMs
    • nextWakeAtMs: Guard j.state.nextRunAtMs
    • isJobDue: Guard job.state.nextRunAtMs

This replaces individual PRs for the same underlying issue.

Greptile Overview

Greptile Summary

This PR aims to harden cron job handling by adding optional chaining (job.state?.…) at several access sites in the cron CLI listing and the cron service scheduling helpers, preventing crashes when job.state is missing.

The changes fit into the existing cron architecture by making both the CLI display layer (src/cli/cron-cli/shared.ts) and the scheduler logic (src/cron/service/jobs.ts) more tolerant of partially-migrated or legacy job records.

However, there are still remaining unguarded job.state dereferences in computeJobNextRunAtMs that can throw, so the “all accesses” guarantee is not fully met yet.

Confidence Score: 3/5

  • This PR is close to safe to merge, but still contains at least one unguarded job.state dereference that can cause runtime errors in the edge cases it targets.
  • Most updated call sites correctly use optional chaining, but computeJobNextRunAtMs still accesses job.state.lastStatus and job.state.lastRunAtMs directly; if job.state can truly be undefined during creation/migration, this leaves the original crash path intact.
  • src/cron/service/jobs.ts

(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!

Systematically add optional chaining (?.) to all job.state accesses
to prevent runtime errors when state object is missing or incomplete.

This is a comprehensive fix for a class of issues where job.state could
be undefined during job creation, data migration, or edge cases.

Changes:
- src/cli/cron-cli/shared.ts:
  - formatStatus: Guard job.state.runningAtMs and lastStatus
  - printCronList: Guard job.state.nextRunAtMs and lastRunAtMs

- src/cron/service/jobs.ts:
  - computeJobNextRunAtMs: Guard job.state.lastStatus and lastRunAtMs
  - nextWakeAtMs: Guard j.state.nextRunAtMs
  - isJobDue: Guard job.state.nextRunAtMs

This replaces individual PRs for the same underlying issue.
@openclaw-barnacle openclaw-barnacle bot added the cli CLI command changes label Feb 5, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +194 to +197
job.enabled ? formatRelative(job.state?.nextRunAtMs, now) : "-",
CRON_NEXT_PAD,
);
const lastLabel = pad(formatRelative(job.state.lastRunAtMs, now), CRON_LAST_PAD);
const lastLabel = pad(formatRelative(job.state?.lastRunAtMs, now), CRON_LAST_PAD);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Falsy timestamp treated as unset
formatRelative uses if (!ms) return "-";, so passing job.state?.nextRunAtMs / lastRunAtMs will render 0 as "-" (even though it’s a valid numeric timestamp). If these fields can ever be 0 (e.g. default/uninitialized from older data), the CLI output will be wrong. Safer check is ms == null (null/undefined) rather than falsy.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/cron-cli/shared.ts
Line: 194:197

Comment:
**Falsy timestamp treated as unset**
`formatRelative` uses `if (!ms) return "-";`, so passing `job.state?.nextRunAtMs` / `lastRunAtMs` will render `0` as "-" (even though it’s a valid numeric timestamp). If these fields can ever be `0` (e.g. default/uninitialized from older data), the CLI output will be wrong. Safer check is `ms == null` (null/undefined) rather than falsy.


How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

Additional Comments (1)

src/cron/service/jobs.ts
Optional chaining incomplete
computeJobNextRunAtMs still dereferences job.state directly (job.state.lastStatus / job.state.lastRunAtMs). This contradicts the PR’s stated goal (guard all job.state accesses) and can still throw when a job is created/migrated with state missing. Consider switching these to job.state?.lastStatus / job.state?.lastRunAtMs (or ensure state is always initialized before calling this).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cron/service/jobs.ts
Line: 52:54

Comment:
**Optional chaining incomplete**
`computeJobNextRunAtMs` still dereferences `job.state` directly (`job.state.lastStatus` / `job.state.lastRunAtMs`). This contradicts the PR’s stated goal (guard *all* `job.state` accesses) and can still throw when a job is created/migrated with `state` missing. Consider switching these to `job.state?.lastStatus` / `job.state?.lastRunAtMs` (or ensure `state` is always initialized before calling this).


How can I resolve this? If you propose a fix, please make it concise.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not include

@tyler6204
Copy link
Member

Superseded by #11641 (merge commit: 8fae55e). Closing to reduce duplicate PR noise. Please open a new PR only if there is additional scope beyond this fix.

@tyler6204 tyler6204 closed this Feb 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants