Skip to content

Fix the ActionMailer error handlers in delivery jobs, and log a failed email once - #21

Merged
ndbroadbent merged 4 commits into
mainfrom
nathan/mailer-error-handling
Oct 17, 2026
Merged

ndbroadbent merged 4 commits into
mainfrom
nathan/mailer-error-handling

Conversation

@ndbroadbent

@ndbroadbent ndbroadbent commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Sixth of twelve stacked PRs for 0.2.0.

What this fixes

  • Every email that failed in ActionMailer::MailDeliveryJob raised NameError (undefined method 'log_and_reraise_error' for class) instead of the real error. The job passes the error to the mailer class, and the handlers were only defined on instances. They're now defined on the class too.
  • Devise tokens were logged in mail job arguments. Devise passes confirmation, password reset and invitation tokens to deliver_later as plain strings, which key-based filtering can't find. For MailDeliveryJob, job lines now log only the mailer, the mail and the delivery method as arguments. Other jobs' arguments are logged as before (when the job's log_arguments is on).
  • A failed delivery is one structured error line. The mailer logged it twice, and a third time when the error reached the mailer class.
  • A delivery job whose mailer let the error through succeeded. In Raise mode (the default for standard errors), log_and_report_error raises the error. In a delivery job it then reached the mailer class's handler, which saw that it was already logged and returned, so the job succeeded with no retry and no report. The class handler now raises an error the mailer already logged, as deliver_now would, without logging it again. An error the class handler handles itself isn't marked, so a mailer given the same error object again still handles it.
  • LogStruct.handle_exception takes log: (default true), for callers whose own line already logged the error.
  • The docs list ActionMailer's error line.
  • Each test gets its own copy of the whole configuration. The code examples left string scrubbers, lograge options, salts, filter settings, the error handling mode and error_reporting_handler set for later tests, so the mailer tests failed when one ran first.
  • The filtering docs say the mail argument filtering applies while the ActiveJob integration is on.

Testing

Every regression test fails without its fix. Sorbet and the unit suite (308 tests) pass at this branch's tip.


Stack for 0.2.0, merged in order: #16 → #17 → #18 → #19 → #20 → #21 → #22 → #23 → #24 → #25 → #26 → #27

ActionMailer::MailDeliveryJob passes a delivery error to the mailer
class (handle_exception), and Rails looks the rescue_from handler up on
the class. LogStruct's handlers (log_and_reraise_error, and the
log_and_ignore_error / log_and_report_error that apps use in their own
rescue_from lines) were only instance methods, so every email that
failed to send in a delivery job raised "NameError: undefined method
'log_and_reraise_error' for class", with the real error as its cause.

Define them on the class as well. An error the mailer's own handler
already logged and re-raised is marked, so the class handler re-raises
it without logging or reporting it twice.
A MailDeliveryJob's arguments include the mail's own arguments, which can
be secrets with no key to filter on: Devise passes its confirmation,
password reset and invitation tokens to the mailer, and deliver_later puts
them in the job. ActiveJob lines logged them on enqueue, start and finish.
The mailer's handler logged a failed delivery twice: its structured line,
then the same struct again before reporting. In Log and Report modes
handle_exception logged it a third time. In Raise mode (the default for
standard errors) handle_exception raised before the error was marked as
logged, so when it reached the mailer class (MailDeliveryJob) the class
handler logged it again.

handle_exception takes log: false for callers that have already logged
the error, and the mailer marks the error as logged straight after its
structured line.
In Raise mode (the default for standard errors), a mailer whose rescue_from
calls log_and_report_error has handle_exception raise the error. In a delivery
job the error then reached the mailer class's handler, which saw that it was
already logged and returned without raising, so the job succeeded and the error
was never reported. The class handler now raises an error the mailer already
logged, as deliver_now does, without logging it again. The marks use
LogStruct::LoggedErrors instead of an instance variable on the error.

The tests now give each test its own copy of the whole configuration. The code
examples left string scrubbers, lograge options, salts, filter settings and the
LogStruct error handling mode set for later tests. The docs say the mail
argument filtering applies while the ActiveJob integration is on.
@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 9 billable files and costs up to $2.25.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 37 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available. Your 53 included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 0cb443b7-5ce3-49c9-89ab-cce961e86590
📥 Commits

Reviewing files that changed from the base of the PR and between 025131f and 9dad347.

📒 Files selected for processing: 9
  • docs/app/docs/filtering-sensitive-data/page.tsx
  • docs/lib/integration-helpers.ts
  • lib/log_struct/concerns/error_handling.rb
  • lib/log_struct/integrations/action_mailer/error_handling.rb
  • lib/log_struct/integrations/active_job/log_subscriber.rb
  • test/log_struct/error_handling_test.rb
  • test/log_struct/integrations/action_mailer/error_handling_test.rb
  • test/log_struct/integrations/active_job/log_subscriber_test.rb
  • test/test_helper.rb
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@ndbroadbent
ndbroadbent changed the base branch from nathan/good-job-workers to main October 17, 2026 23:13
@ndbroadbent
ndbroadbent merged commit c9a1947 into main Oct 17, 2026
1 check passed
@ndbroadbent
ndbroadbent deleted the nathan/mailer-error-handling branch October 18, 2026 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant