diff --git a/src/cron/service/store.ts b/src/cron/service/store.ts index 82ea5e0bfa65..58eed3b0c6a8 100644 --- a/src/cron/service/store.ts +++ b/src/cron/service/store.ts @@ -104,13 +104,13 @@ export async function ensureLoaded( previousJobsById.set(job.id, job); } const loaded = await loadCronJobsStoreWithConfigJobs(state.deps.storePath); - const loadedJobs = (loaded.store.jobs ?? []) as unknown as CronJob[]; + // Persisted cron rows are validated lazily, so treat them as raw records at the + // store boundary and only trust the CronJob shape after validation below. + const loadedJobs = (loaded.store.jobs ?? []) as unknown as Record[]; const jobs: CronJob[] = []; const quarantinedConfigJobs: QuarantinedCronConfigJob[] = [...loaded.invalidConfigRows]; - for (const [index, job] of loadedJobs.entries()) { - const decodedRaw = job as unknown as Record; - const rawConfigJob = loaded.configJobs[index] ?? structuredClone(decodedRaw); - const raw = decodedRaw; + for (const [index, raw] of loadedJobs.entries()) { + const rawConfigJob = loaded.configJobs[index] ?? structuredClone(raw); const sourceIndex = loaded.configJobIndexes[index] ?? index; const runtimeEntry = loaded.configJobRuntimeEntries[index]; // Accept old `jobId` rows at the raw boundary only; the in-memory store @@ -129,11 +129,8 @@ export async function ensureLoaded( "cron: job has invalid persisted sessionTarget; run openclaw doctor --fix to repair", ); } - const hydrated = - normalized && typeof normalized === "object" ? (normalized as unknown as CronJob) : job; - const invalidReason = getInvalidPersistedCronJobReason( - hydrated as unknown as Record, - ); + const hydratedRaw = normalized ?? raw; + const invalidReason = getInvalidPersistedCronJobReason(hydratedRaw); if (invalidReason) { const quarantineEntry: QuarantinedCronConfigJob = { sourceIndex, @@ -157,6 +154,8 @@ export async function ensureLoaded( warnInvalidPersistedCronJob({ state, raw, index: sourceIndex, reason: invalidReason }); continue; } + // Validated above, so the raw record is now a trusted CronJob. + const hydrated = hydratedRaw as unknown as CronJob; jobs.push(hydrated); invalidateStaleNextRunOnScheduleChange({ previousJobsById, hydrated }); }