fix(mailing-list): report Flodesk failures to Rollbar, drop dead subscribed? - #2958
Merged
mroderick merged 3 commits intoSep 28, 2026
Merged
Conversation
mroderick
approved these changes
Sep 28, 2026
mroderick
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed and verified in a worktree of the PR branch:
- The diagnosed bug is real: the client's Faraday connection uses
response :raise_error, so the oldrescue Faraday::Errorclause returned aFlodeskErrorinstead of raising —Services::MailingList'srescue Flodesk::FlodeskErrornever fired, and a 503 leftsubscribe/unsubscribereturning a truthy error object silently. The old client spec only tested 200 responses, so the failure path was untested. - The timeout account is accurate: old code called
e.response_body['message']unguarded,Faraday::TimeoutErrorhas a nil body →NoMethodError→ DelayedJob retry (max_attempts = 3inconfig/initializers/delayed_job.rb). - The
Rollbar.errorchoice is justified: rollbar 3.8.0 shipsenable_rails_error_subscriberdefaulting tofalse(verified in the installed gem'slib/rollbar/configuration.rb:108), andconfig/initializers/rollbar.rbdoesn't enable it, soRails.error.reportnever reaches Rollbar here. The call matches theInvitationManagerprecedent. - No leftover references to
subscribed?orFlodesk::ACTIVE; no caller consumes the subscribe/unsubscribe return value. - Ran the PR's exact suite (
spec/lib spec/services spec/controllers/memberplus both feature specs): 249 examples, 0 failures.rubocopon both changed lib files: clean.
On the flagged behaviour change (a network blip now drops a sync after one attempt instead of being retried 3× by accident): I agree with the choice made here. Re-raising timeouts would end in the DelayedJob failure path, whose Rails.error.report is dead in this app, so the final failure would be invisible outside the DJ table. Every failure visible in Rollbar beats silent retries that can still exhaust. If Flodesk reliability becomes a real problem, re-raising 5xx/timeouts plus enabling the error subscriber is the follow-up, not this PR.
mroderick
enabled auto-merge
September 28, 2026 06:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2946
Flodesk failures on subscribe/unsubscribe are now reported to Rollbar with the list id and email, and still swallowed so the async job doesn't re-fail.
Services::MailingList#subscribed?andFlodesk::Client#subscribed?(plus theACTIVEconstant only they used) are removed with their specs.Two things turned out different from the issue's plan:
Rollbar.errorinstead ofRails.error.report. rollbar 3.8.0 ships its Rails error subscriber disabled (enable_rails_error_subscriberdefaults tofalse) andconfig/initializers/rollbar.rbdoesn't turn it on, soRails.error.reportnever reaches Rollbar in this app. This usesRollbar.error(e, list_id:, email:)likeInvitationManagerdoes. (The same applies to theRails.error.reportinApplicationJob; that one only surfaces because it re-raises. Happy to switch both over by enabling the subscriber instead if you'd prefer, but that also changes how Rollbar captures uncaught exceptions app-wide, so I left it out of this PR.)Flodesk::Client#requestnow raisesFlodeskErrorinstead of returning it. It rescuedFaraday::Errorand returned the error object, so therescue Flodesk::FlodeskErrorinMailingListnever ran for API errors (the only code that looked at the returned error wassubscribed?). Timeouts and connection failures also crashed with aNoMethodErrorbecause they have no response body; the message now falls back to the Faraday error's. Every caller goes throughServices::MailingList, which rescues it. One behaviour change to be aware of: a timeout or connection failure used to crash the job with thatNoMethodError, so Delayed Job retried it (up tomax_attempts = 3). Now it's reported and swallowed like any other Flodesk failure, per the "keep swallowing" policy in the issue, which means a single network blip can drop a subscribe or unsubscribe. If you'd rather keep those retries, I can swallow only 4xx responses and re-raise timeouts and 5xx so Delayed Job retries them; say the word and I'll push that.The email address goes into the Rollbar context as the issue asks; Rollbar person tracking already sends member emails with request errors here, so it isn't a new kind of data, but say if you'd rather send the member id.
Testing
raise_error+jsonmiddleware: a 503 raisesFlodeskErrorwith the API's message and status; a timeout raisesFlodeskErrorServices::MailingListspecs: a failed subscribe/unsubscribe callsRollbar.errorwith the list id and email and doesn't raisebundle exec rspec spec/lib spec/services spec/controllers/member spec/features/subscribing_to_newsletter_spec.rb spec/features/manage_contact_preferences_spec.rb: 249 examples, 0 failures; rubocop clean