Retry a failed run instead of exiting, with timeouts (1.4.0) #9

Merged
joeyr merged 1 commit from feat/survive-failed-runs into main 2026-10-05 20:44:06 -05:00
Owner

One error used to stop the whole program: Workflow.Run called Logger.Fatal, which exited the process, scheduler included, so a Jellyfin restart or a mail server blip lost that week's email. Requests also had no timeouts.

  • newsletter.go: runOnce returns errors. Run retries after 5, 10 and 15 minutes, then logs an error and emails a one-line notice to the new optional email.failure_notice_recipient. It stops retrying once some recipients have the email (SendError.Sent > 0), so nobody gets it twice. A dry run gets one attempt and no notice.
  • smtp/sender.go: one sendToRecipients loop shared by the newsletter and the notice, returning a SendError that names the missed recipients. mime_message.go leaves out an empty part (the notice is text only).
  • jellyfin/movies.go, series.go: a folder whose request fails now fails the run instead of being dropped silently; a folder Jellyfin doesn't have is still just a warning.
  • Timeouts: one minute for HTTP (main.go); 30 s to dial SMTP and a 10-minute session deadline (smtp/client.go).
  • Tests: the retry schedule, giving up, no retry after a partial send, a retry when nobody got it, Run surviving an unreachable Jellyfin, a dry run failing at once, notice sent / not configured, a text-only message, and folder-error propagation. Also a regression dry run of the built image against the real Jellyfin: 12 s, 86 KB email.
  • VERSION 1.4.0, CHANGELOG, CLAUDE.md, config-example.yml.

🤖 Generated with Claude Code

One error used to stop the whole program: `Workflow.Run` called `Logger.Fatal`, which exited the process, scheduler included, so a Jellyfin restart or a mail server blip lost that week's email. Requests also had no timeouts. - `newsletter.go`: `runOnce` returns errors. `Run` retries after 5, 10 and 15 minutes, then logs an error and emails a one-line notice to the new optional `email.failure_notice_recipient`. It stops retrying once some recipients have the email (`SendError.Sent > 0`), so nobody gets it twice. A dry run gets one attempt and no notice. - `smtp/sender.go`: one `sendToRecipients` loop shared by the newsletter and the notice, returning a `SendError` that names the missed recipients. `mime_message.go` leaves out an empty part (the notice is text only). - `jellyfin/movies.go`, `series.go`: a folder whose request fails now fails the run instead of being dropped silently; a folder Jellyfin doesn't have is still just a warning. - Timeouts: one minute for HTTP (`main.go`); 30 s to dial SMTP and a 10-minute session deadline (`smtp/client.go`). - Tests: the retry schedule, giving up, no retry after a partial send, a retry when nobody got it, `Run` surviving an unreachable Jellyfin, a dry run failing at once, notice sent / not configured, a text-only message, and folder-error propagation. Also a regression dry run of the built image against the real Jellyfin: 12 s, 86 KB email. - VERSION 1.4.0, CHANGELOG, CLAUDE.md, config-example.yml. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Retry a failed run instead of exiting, with timeouts (1.4.0)
All checks were successful
CI / gate (pull_request) Successful in 1m1s
CI / lint (pull_request) Successful in 55s
CI / image (pull_request) Has been skipped
6b8d305dcd
Any error in Workflow.Run called Logger.Fatal, which exited the process,
scheduler included: the container restarted and waited for next week,
so a Jellyfin restart or a mail server blip at 5 PM Friday lost that
week's email. HTTP used http.DefaultClient and SMTP had no deadlines,
so a request that never answered stalled the program for good.

runOnce now returns its errors. Run retries after 5, 10 and 15 minutes,
then logs an error and emails a one-line notice to the new optional
email.failure_notice_recipient. sendToRecipients returns a SendError
naming the recipients it missed; retry stops once Sent > 0, since another
attempt would send the email to those recipients twice. A dry run is
run by hand, so it gets one attempt and no notice.

GetRecentlyAddedMovies and GetNewlyAddedSeries used to drop any folder
whose request failed, so the email quietly lacked it. They now return
the error (and the run retries); a folder Jellyfin doesn't have is
still skipped with the existing warning.

Timeouts: one minute for HTTP; 30 s to dial SMTP and 10 minutes for
the session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
joeyr merged commit 700b26ca82 into main 2026-10-05 20:44:06 -05:00
joeyr deleted branch feat/survive-failed-runs 2026-10-05 20:44:06 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
joeyr/jellyfin-newsletter!9
No description provided.