1.25.2 — Manual job retry publishes a runnable, linked replacement
Editorial identity incomplete
2026-09-11
Retrying a failed, canceled or expired job now publishes a replacement with a fresh expiration and leaves the original as a terminal record linked to it. Before, an expired job's retry inherited its past deadline and expired before running, and the original was reset to pending with its diagnostics wiped. A second retry is refused while the replacement is still live.
Changed
- Manual retry (
retry_requestaction,POST /api/jobs/retry,JobManager.retry_job, the assistantretry_jobtool) publishes a replacement job and leaves the original untouched — its status, attempt count and error diagnostics are no longer reset topending. - The replacement's expiration is the larger of
JOBS_DEFAULT_EXPIRES_SECand the window the original was published with, extended bydelay; the original'sexpires_atis never reused. - Original and replacement reference each other:
metadata.retried_ason the original,metadata.retried_fromon the replacement, andnew_job_idin the original'sretryevent. - A second retry is refused with
Job already retried as <id>while that replacement is pending, running or completed; allowed again once it has failed, been canceled or expired. This is double-submit protection, not a locked exactly-once guarantee. JobManager.retry_jobacceptsexpiredjobs and returns the replacement id (orFalse), matching the service path.- A negative retry
delayis refused instead of silently shortening the replacement's lifetime. - A non-numeric
delaysent throughretry_requestreturnsstatus: falseinstead of a 500. - Removed
JobActionsService.pause_job/resume_job— undocumented, unreferenced, and carrying the same reset-and-copy-expiry defect;cancel_job+retry_jobcover the same need.
Fixed
- Retrying an expired job produced a replacement that expired before it ran.
- Retrying left the original row
pendingwith its diagnostics erased — a zombie that arequeue_db_pendingsweep would have executed a second time. - A delayed retry longer than the default job lifetime expired before it was due.
Upgrade notes
- No migration. Code that detected "retry in flight" by watching the original job flip to
pendingshould readmetadata.retried_asinstead — the original now staysfailed/canceled/expired.