EOT Renewal / Reminder Isn't Sent when Processing s2member auto eot system via cron

Hi! When repeatedly using https://myurl/?s2member_auto_eot_system_via_cron=1 I can force wordpress/s2member to process all pending to expire users to be demoted.

They are demoted after I run it enough times, I receive the “(s2Member / API Notification Email) - EOT/Deletion” email but the subscribers do NOT receive their corresponding " EOT Renewal/Reminder Emails" at the same time.

Those messages are very important because they alert the user they lost privileges and, of course, are a strong incentive for them to subscribe again.

I’d appreciate if anybody knows if there’s another c - url that we can do to force those messages to be sent as well, since sometimes those warnings are sent a few hours later, but the “s2member_auto_eot_system_via_cron” doesn’t seem to include that routine.

I wonder if there’s a delay for those notifications, because they started being processed a few hours after their EOT, and only a few users every few minutes (irregular intervals).

I have changed the auto_eot “per process” to 50 (which helps with the EOT cron), but I feel it does not interfere with this other task, which must be triggered somewhere else. If I get a clear head soon I’ll try to look into the code again for clues, but I don’t program php yet, so I usually only hack existing code by making tiny modifications whenever possible :wink:

I wish there were more people acquiring the pro version of the plugin to help with its development. Sorry for not having enough technical knowledge to help better this far. Maybe in the future :innocent:

Thanks a lot no matter what, as usual.

Hi Sim.

Yeah, that’s a good improvement. Raise it to 100 or more, too, if your server can handle it. You can also raise the EOT reminders per process.

<?php
add_filter('ws_plugin__s2member_auto_eot_system_per_process', 's2_eots_per_process');
add_filter('ws_plugin__s2member_pro_eot_reminders_per_process', 's2_eots_per_process');
function s2_eots_per_process() {
   return 100;
}

See also this article: http://aaronjerad.com/blog/fix-s2member-eot-reminder-emails-not-sent/

I hope that helps! :slight_smile:

1 Like

Thanks a lot!

I had that done already (not as elegantly as in your example, which I will surely use instead of the code I currently have), but running the cron through that url doesn’t trigger it (it does trigger the EOT but not the pro messages).

Is there a different Curl I should use to trigger the pro_eot besides the auto_eot?

1 Like

No, that URL you posted is correct.

Did you use both filters in your code, or just one? You mentioned auto_eot_system_per_process, but not pro_eot_reminders_per_process.

In your log files, what do the EOT ones say? WP Admin > s2Member > Log Files

:slight_smile:

2 Likes

The pro_eot_reminders_per_process were in the functionality.php (it’s the hack thing I use to add those pieces of code).

This is the code I had before:

// Increase per process SQL records scanned for EOT Reminder Emails

		add_filter ('ws_plugin__s2member_pro_eot_reminders_per_process', 'aamet_set_eot_per_process');
		
		function aamet_set_eot_per_process(){
			$per_process = 50;
			return $per_process;
			}

Just in case, I changed it to 200 and used your code now, we’ll see what happens :wink:

add_filter('ws_plugin__s2member_auto_eot_system_per_process', 's2_eots_per_process');
add_filter('ws_plugin__s2member_pro_eot_reminders_per_process', 's2_eots_per_process');
function s2_eots_per_process() {
return 200;
}
2 Likes

Fingers crossed. :smiley:

2 Likes

Where did you put that file? In /wp-content/mu-plugins/?

1 Like

No, I have a plugin called functionality, then I just edit the file functionality.php on the plugin editor. I set that thing up a long time ago, don’t remember exactly how :man_facepalming:t2:

I can see the code runs, though, since when I run the cron curl I get the 200 instead of the previous 50 and if I have the functionality.php disabled I go back to 6.

I feel that the auto_eot curl does what it says, it runs the auto_eot but not the pro_eot_reminders.

Gotcha. Thanks for the details.

What do the log files say?

1 Like

I didn’t check for this specific issue because the messages (I believe all of them, didn’t check one by one) end being sent at some point, but it usually takes several hours. I felt it was funny because I get the EOT emails right away but the messages to the users may take somewhere between one hour and one day after they already lost access.

I wish there was a way to have them notified right away instead, or to force everything to happen manually every now and then, because about once or twice a month I downgrade multiple users at once and it would be great if I could message all those EOT users right away, so they’d have at least an hour to contact me and pay again without losing some specific privileges I assign to them manually on my cloud storage. :innocent:

I see.

Well, now that you’re processing more at a time, you’re catching up, so your queue should be much shorter and quicker soon.

About demoting several at a time, you could use the user import to modify their EOT time to next day, instead of changing their role manually. If you just bulk change the role, the EOT handler doesn’t get triggered.

1 Like

Oh yes, I always set the EOT.

I usually set the EOT to 25 hours (it seems better to use any value above 24 hours from my experience, plus the user gets two warnings, one that their account will expire in less than 24 hours, a second that their account expired) but there was a new version of my content being released shortly after, so I had to set the EOT to a few dozen people to “now” instead, so the users that were late on their payments (or cancelled, I use other platforms like Patreon and I process those users manually, being Patreon usually once a month for 90% of the declines etc) would not have access to the new version without paying for it. :wink:

1 Like

Just found out something interesting! If the Paid Subscr. ID is empty (even if it’s a user we upgraded manually as a gift or because they paid us using a method that’s not managed by s2member) the Reminder Scan field is not populated, but it seems that the EOT is demoting the users, thankfully!

I felt it was worth mentioning, just in case the plugin is changed in the future, so the EOT doesn’t get broken in those cases for any reason.

Also, perhaps that’s the cause for the pro EOT reminders not being sent to some users? On the other hand, since we can set those messages to be sent after EOT, would that field interfere at all, considering the field is cleared up at EOT?

Anyway, I felt it was worth mentioning for reference. :wink:

Thanks again for everything and Happy New Year’s Eve!

:snowman::christmas_tree::calendar::fireworks::champagne::clinking_glasses::snowman:

Here’s an update to that hack:

<?php
add_filter('ws_plugin__s2member_auto_eot_system_per_process', 's2_eots_per_process', 99, 2);
add_filter('ws_plugin__s2member_pro_eot_reminders_per_process', 's2_eots_per_process', 99, 2);
function s2_eots_per_process($per_process, $vars = []) {
   return 50;
}
1 Like

Updated my code, thanks! I’ll let you know. Still need to update from Dec 2024 to the latest. I need my modifications in place, otherwise I’d have done it already.

I was finally able to solve a housing situation (well, not yet, but soon) and I’ll gradually have more mental space to work on this. Thanks!!!

:slightly_smiling_face:

1 Like

I’ve recently switched a clients site to using the system cron rather than wp-cron with

define( 'DISABLE_WP_CRON' , true);

And replaced it with the following bash script run every three minutes:

#!/usr/bin/env bash

cd /var/www/DOMAINNAMEHERE.org/public_html/
wp cron event run --due-now --quiet >/dev/null 2>&1

But I am now getting the error message within s2member

s2Member EOT reminders need attention. The EOT reminder scheduler has been unhealthy for 1 day. Review EOT reminder status.

is the “wp cron event run --due-now --quiet” sufficient or is there something specific about running the /?s2member_auto_eot_system_via_cron=1 that needs to be executed??

Yes, running wp cron event run --due-now every three minutes should work fine. You don’t need to call the /?s2member_auto_eot_system_via_cron=1 URL separately.

The EOT reminders now have their own scheduler, independent of the Auto-EOT setting, so they should run normally through WP-Cron.

I’m curious, though: was there a particular reason you switched to a system cron? Were you having trouble with WP-Cron, even after the recent improvements to s2Member’s EOT processing? The updated system is much better at keeping up with the queue, so a separate cron shouldn’t generally be necessary.

If the site doesn’t get much traffic, though, having a server cron periodically trigger WP-Cron is a good idea, since WordPress normally relies on visits to run scheduled tasks.

Regarding the warning, could you click the “Review EOT reminder status” link and share a screenshot of what it shows under EOT Reminder Status?

2026-10-19_03-38-45_s2Member%20PayPal%20Options%20%E2%80%B9%20s2Member%20Membership%20Plugin%20for%20WordPress%20%E2%80%94%20WordPress_region

That should tell us more about what’s happening with the reminder scheduler.

You could also enable logging and check eot-reminders.log after a few cron runs to see if anything helpful is mentioned. WP Admin > s2Member > Log Files

Let’s see what those show.

:slight_smile:

1 Like

My clients sites were regularly becoming unresponsive because multiple wp-cron.php processes were saturating the CPUs to 100% and not finishing before the next run started a minute later. This left their whole box occasionally unresponsive, requiring manual intervention. Looking into what is making the wp-cron take so long to process is the preferable fix but until then I’m looking to treat the symptoms, which has unearthed an unexpected issue…

They have a small number (<6) of sites running s2member and part of the solution is to run all their wp-cron’s via a bash script so they run serially (one at a time) to further reduce the saturation of their server. Relying on wp-cron would mean sometimes more than one wp-cron would be running in parallel which would add to the problems. So now all except one site is relying only on the following command line wp-cli

#!/usr/bin/env bash

cd /var/www/DOMAINNAMEONE.org/public_html/
wp cron event run --due-now --quiet >/dev/null 2>&1

but it looks like the wp cron event run --due-now --quiet is not completing the EOT in a way that the Automatic Behavior Status display finds acceptable, these sites are reporting something like:

(The Last completed run is the time since the last time the cron bash script ran which included /s2member_auto_eot_system_via_cron=1)

But on their most important site I included the /s2member_auto_eot_system_via_cron=1 and when this is included the Automatic Behavior Status is reporting aok

#!/usr/bin/env bash

cd /var/www/DOMAINNAMEHERE.org/public_html/
wp cron event run --due-now --quiet >/dev/null 2>&1
curl -sS --max-time 60 "https://www.DOMAINNAMEHERE.org/?s2member_auto_eot_system_via_cron=1" >/dev/null 2>&1

So something about wp cron event run --due-now --quiet is not processing the EOT properly and only the /s2member_auto_eot_system_via_cron=1 is doing it.

Thanks for explaining the server issues. Running the sites’ cron jobs serially makes sense given the CPU saturation you’ve been experiencing.

Your screenshots show Auto-EOT configured as “Yes (but, I’ll run it with my own Cron Job)”.

With that setting, s2Member deliberately doesn’t schedule Auto-EOT through WP-Cron. It expects you to trigger it through the dedicated HTTP URL instead.

I’d recommend that you try changing the Auto-EOT setting to “Yes (enable via WP-Cron)”.

Also, your original warning was about EOT Reminders, but these last screenshots are from the Automatic Behavior (Auto-EOT) section. Those are two separate systems, with independent schedules.

2026-10-19_18-16-36_s2Member%20PayPal%20Options%20%E2%80%B9%20s2Member%20Membership%20Plugin%20for%20WordPress%20%E2%80%94%20WordPress_region

Could you please show me the EOT Reminder Status section? That’s the one we need to check to understand why you’re getting the reminder scheduler warning.

Also, since your script redirects all output to /dev/null, it may be hiding errors from WP-CLI. It would be worth temporarily running the command without --quiet and without the output redirection to see if anything is reported.

I look forward to your update.

:slight_smile:

Further to my previous post about a more secure way to replace wp-cron with a (potentially) more reliable system cron

To be honest given the CPU saturation they experienced I’ll keep them on the system cron and amend the bash file instead. Some of their sites have a very high number of users which may be behind the problem they’ve been experiencing.

I’ve amended the calling cron task to include flock which will ensure only one copy of the crontab bash script, wp_crontabs_script.sh, is running at any one time because it checks the existence of the file lockfile /tmp/wp-cron.lock

#runs every three minutes
*/3 * * * * /usr/bin/flock -n /tmp/wp-cron.lock /usr/local/bin/wp_crontabs_script.sh

and /usr/local/bin/wp_crontabs_script.sh contains

#!/usr/bin/env bash

cd /var/www/DOMAINNAME1.COM/public_html/
wp cron event run --due-now --quiet >/dev/null 2>&1
curl -sS --max-time 60 "https://www.DOMAINNAME1.COM/?s2member_auto_eot_system_via_cron=1" >/dev/null 2>&1

cd /var/www/DOMAINNAME2.COM/public_html/
wp cron event run --due-now --quiet >/dev/null 2>&1
curl -sS --max-time 60 "https://www.DOMAINNAME2.COM/?s2member_auto_eot_system_via_cron=1" >/dev/null 2>&1

cd /var/www/DOMAINNAME3.COM/public_html/
wp cron event run --due-now --quiet >/dev/null 2>&1
curl -sS --max-time 60 "https://www.DOMAINNAME3.COM/?s2member_auto_eot_system_via_cron=1" >/dev/null 2>&1

So I’ve included the line containing the call to s2member_auto_eot_system_via_cron=1 because each of these sites has their Auto-EOT System set to run from this bash script rather than wp-cron.php with the setting.

Yes (but, I'll run it with my own Cron Job)

It might be worth making the absence of a schedule Auto-EOT when WP-Cron is not used more explicit in the documentation as its not very clear in the current text.