Skip to content

fix(ui): guard against undefined cron job payload in render loop#38766

Closed
luka2chat wants to merge 4 commits into
openclaw:mainfrom
luka2chat:fix/cron-payload-undefined-guard
Closed

fix(ui): guard against undefined cron job payload in render loop#38766
luka2chat wants to merge 4 commits into
openclaw:mainfrom
luka2chat:fix/cron-payload-undefined-guard

Conversation

@luka2chat

Copy link
Copy Markdown

Summary

  • When a cron job's payload field is undefined (e.g. from incomplete API data or legacy jobs), directly accessing job.payload.kind in app-render.ts throws TypeError: Cannot read properties of undefined (reading 'kind').
  • Since this error occurs inside the LitElement render() method, it triggers on every reactive update, creating an infinite error loop that freezes the entire dashboard UI — users cannot switch tabs or interact with the page at all.
  • This PR adds optional chaining (?.) guards to all job.payload access sites in app-render.ts, controllers/cron.ts, and views/cron.ts, with an early-return nothing guard in renderJobPayload().

Reproduction

  1. Have a cron job where the gateway returns a job object with payload: undefined
  2. Navigate to or through the Cron tab in the Gateway Dashboard
  3. Observe the console filling with hundreds of TypeError: Cannot read properties of undefined (reading 'kind') errors
  4. The UI becomes completely unresponsive

Test plan

  • vite build compiles successfully with no type errors
  • controllers/cron.test.ts — all 27 tests pass
  • No regressions introduced (all pre-existing test failures remain unchanged)

Made with Cursor

@greptile-apps

greptile-apps Bot commented Mar 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds defensive ?. optional-chaining guards and an early-return nothing guard across the three cron-related files to prevent TypeError: Cannot read properties of undefined (reading 'kind') from crashing the LitElement render loop when a job's payload is undefined at runtime. The render-loop fix in views/cron.ts directly stops the infinite re-render and UI freeze described in the bug report.

Key observations:

  • All guard placements are logically correct — in every guarded ternary the safe-access arm is only entered when the optional chain resolves to the expected value, so subsequent non-optional accesses (job.payload.text, job.payload.message, etc.) remain safe.
  • Defaulting payloadKind to "agentTurn" when payload is undefined (job.payload?.kind ?? "agentTurn" in controllers/cron.ts) is a silent fallback that could cause a user editing a legacy job to save an incorrectly typed payload. This choice should be documented with a comment, or a more explicit error/disabled state should be shown.
  • CronJobBase.payload is still typed as required (payload: TPayload), meaning TypeScript will not catch future access sites that lack a ?. guard. A more complete fix would mark the field as optional (payload?: TPayload) in src/cron/types-shared.ts, surfacing the undefined case to the type-checker throughout the codebase.

Confidence Score: 4/5

  • Safe to merge as a targeted hotfix; the UI freeze is resolved and no regressions are introduced, though the type definition gap leaves the door open for similar issues elsewhere.
  • The runtime bug fix is correct and directly addresses the reported infinite error loop. All optional-chaining patterns are logically sound. Score is 4 rather than 5 because the underlying CronJobBase type still marks payload as required, meaning the TypeScript compiler won't flag future unsafe accesses, and the arbitrary "agentTurn" default in jobToForm could silently produce incorrect job edits for legacy data.
  • Pay close attention to ui/src/ui/controllers/cron.ts — specifically the payloadKind fallback to "agentTurn" and the broader type-safety gap in src/cron/types-shared.ts where payload should arguably be marked optional.

Last reviewed commit: 63ec946

Comment thread ui/src/ui/controllers/cron.ts
Comment thread ui/src/ui/controllers/cron.ts Outdated
@luka2chat luka2chat force-pushed the fix/cron-payload-undefined-guard branch from 63ec946 to 8f8de32 Compare March 7, 2026 10:36
When a cron job's payload field is undefined, directly accessing
job.payload.kind throws a TypeError. Since this happens inside the
LitElement render() method, it triggers on every reactive update,
creating an infinite error loop that freezes the entire dashboard UI.

Add optional chaining guards to all job.payload access sites in
app-render.ts, controllers/cron.ts, and views/cron.ts.

Made-with: Cursor
Address review feedback: document why payloadKind defaults to
"agentTurn" when job.payload is undefined.

Made-with: Cursor
@luka2chat luka2chat force-pushed the fix/cron-payload-undefined-guard branch from 8f8de32 to 8106b06 Compare March 7, 2026 11:34
Same undefined payload issue as the UI fix, but in the CLI
cron list table renderer. Prevents TypeError when listing
cron jobs with missing payload data.

Made-with: Cursor
@openclaw-barnacle openclaw-barnacle Bot added the cli CLI command changes label Mar 7, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 22, 2026
@vincentkoc vincentkoc added the close:superseded PR close reason label Apr 25, 2026
@vincentkoc vincentkoc self-assigned this Apr 25, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Thanks for the earlier UI-side guard here.

I’m closing this as superseded by #71534, which fixes the root persisted-row shape by hydrating flat legacy cron jobs into canonical schedule, sessionTarget, and payload objects before startup/list/UI paths see them. That keeps the UI from receiving the malformed rows that caused the render-loop .kind crash.

If there is another current-main path that still returns a cron job without payload/schedule, reply with that payload and I can reopen this quickly.

@vincentkoc vincentkoc closed this Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui cli CLI command changes close:superseded PR close reason size: XS stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants