fix(cron): coerce pad value so cron list survives non-string job id#70183
fix(cron): coerce pad value so cron list survives non-string job id#70183truffle-dev wants to merge 2 commits into
Conversation
Greptile SummaryDefensive coercion in the Confidence Score: 5/5Safe to merge — minimal, well-scoped render-side defensive fix with regression test coverage. The change is a single-function type widening with a correct ternary coercion ( No files require special attention. Reviews (1): Last reviewed commit: "fix(cron): coerce pad value so cron list..." | Re-trigger Greptile |
|
Thanks for the careful version of this. Closing as superseded by the maintainer patch: malformed persisted cron job IDs are repaired through doctor, with regression coverage, instead of making cron list absorb corrupt store rows. |
|
Thanks for the note. Store-layer repair via doctor is the more principled shape; it removes the malformed row instead of pushing defensive coercion into every downstream renderer. |
Summary
openclaw cron listcrashes withTypeError: value.padEnd is not a functionwhenever a cron entry surfacesjob.idas a non-string (numeric or undefined, from older-schema or corrupted store data).--jsonoutput is unaffected, which is how the reporter routed around it.openclaw cron listcrashes with TypeError: value.padEnd is not a function #70128 confirmed on 2026.4.15 and 2026.4.21 (same unchangedpadimplementation in both shipped dists).pad()insrc/cli/cron-cli/shared.tsnow widens its parameter type tostring | number | null | undefinedand coerces non-string primitives via a typeof-guardedString()sopadEndalways runs on a string.truncate(), table headers,--jsonoutput, or the store/migration path.CronJob.idschema type staysstring; only the render-side helper is defensive.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
openclaw cron listcrashes with TypeError: value.padEnd is not a function #70128Root Cause (if applicable)
pad()was typed(value: string, width: number)and call sitepad(job.id, CRON_ID_PAD)atsrc/cli/cron-cli/shared.ts:278passesjob.idunguarded.CronJob.idis typedstringat compile time, but older-schema or corrupted entries can surface it as numeric/undefined at runtime. TypeScript does not enforce this for external data, sovalue.padEnd(width)throws on the first non-string row.job.idshapes.openclaw cron listcrashes with TypeError: value.padEnd is not a function #70128 triage by @rafiki270 root-caused to this helper and confirmed the minimal fix; call-site audit showed all otherpad()calls already go through string literals,truncate(), or?? "-"fallbacks, sojob.idat:278is the only unguarded external field.Regression Test Plan (if applicable)
src/cli/cron-cli/shared.test.tsprintCronListmust not throw whenjob.idis numeric or undefined, and must still emit the table row.printCronListtest catches it at the exact seam without needing full gateway/store wiring.User-visible / Behavior Changes
openclaw cron listno longer crashes when a cron entry has a non-stringjob.id. Numeric ids render stringified (e.g.42shows as42); undefined/null ids render as blank padding so the other columns still line up.Diagram (if applicable)
N/A
Security Impact (required)
Yes/No) NoYes/No) NoYes/No) NoYes/No) NoYes/No) NoYes, explain risk + mitigation: N/ARepro + Verification
Environment
Steps
pad()change and keep the two new tests in place.pnpm test src/cli/cron-cli/shared.test.ts.TypeError.Expected
TypeError: value.padEnd is not a function(numeric id) andTypeError: Cannot read properties of undefined (reading 'padEnd')(undefined id).Actual
Stash-bisect output:
Pre-fix:
Post-fix:
Evidence
Human Verification (required)
What you personally verified (not just CI), and how:
.padEndcrash locally via stash-bisect against the new regression tests.src/cli/cron-cli/shared.test.tscases pass post-fix.pnpm check:changed(conflict markers, typecheck core, typecheck core tests, lint core, runtime import cycles, webhook body guard, pairing store/account guards, tests changed) — all green.job.idrenders as stringified digits (42in the output).job.idrenders as blank padding, row still emits.job.idkeeps existing.padEndbehavior (20 original tests still green).openclaw cron listagainst a real gateway with an actually-corrupted store entry; the crash path is a pure render helper so the unit test is the correct seam.pnpm check/pnpm testsweep — pre-commit oxfmt hitEAGAINon this container (process spawn limit across tinypool workers);check:changedcovered the touched surface and format diff was visually verified against existing file conventions.Review Conversations
Compatibility / Migration
Yes/No) YesYes/No) NoYes/No) NoRisks and Mitigations
pad()'s parameter type could mask a genuine non-string leak upstream (e.g. a broken store migration) that would be better fixed at the schema layer.CronJob.idasstring, so this change only affects what the operator sees when corruption already exists. The typeof guard preserves strict-string behavior for well-formed entries, and the fix is isolated enough to revert cleanly if a better upstream guard lands.AI-assisted
check:changedgreen + 22/22 targeted tests.