Initial skills documentation — 25 categories, all SKILL.md + references + scripts
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# WPForms Email Delivery Debugging
|
||||
|
||||
## Common Failure Modes on SiteGround/RunCloud WordPress Hosting
|
||||
|
||||
### 1. PHP Serialization Length Mismatch
|
||||
|
||||
**Symptom:** WP Mail SMTP plugin shows "Authentication failed" or silently drops emails. Debug log shows SMTP connection succeeds but auth fails.
|
||||
|
||||
**Root cause:** When the SMTP password is updated via the database (not the plugin UI), the PHP serialized string length in `wp_options` may not match the actual password length.
|
||||
|
||||
Example: password `apex.track!!` (13 chars) stored as `s:72:"apex.track!!"` (declares 72 chars). PHP unserialization fails and the password field comes back empty.
|
||||
|
||||
**Check:**
|
||||
```sql
|
||||
SELECT LENGTH(JSON_UNQUOTE(JSON_EXTRACT(option_value, '$.smtp.pass'))) as pass_len
|
||||
FROM wp_options WHERE option_name = 'wp_mail_smtp';
|
||||
```
|
||||
If NULL, the serialization is broken.
|
||||
|
||||
**Fix:**
|
||||
```sql
|
||||
UPDATE wp_options
|
||||
SET option_value = REPLACE(option_value,
|
||||
's:72:\"apex.track!!\"',
|
||||
's:13:\"apex.track!!\"')
|
||||
WHERE option_name = 'wp_mail_smtp';
|
||||
```
|
||||
Replace `s:72` with `s:N` where N = actual character count of the password.
|
||||
|
||||
### 2. Invalid Sender Address (Multiple Emails)
|
||||
|
||||
**Symptom:** SMTP server rejects with "X domain is not currently owned by sender" or similar. WPForms notifications specify a `sender_address` that may differ from the SMTP login.
|
||||
|
||||
**Check:**
|
||||
```sql
|
||||
SELECT JSON_EXTRACT(post_content, '$.settings.notifications."1".sender_address')
|
||||
FROM wp_posts WHERE ID = <form_id>;
|
||||
```
|
||||
|
||||
**Fix:** Ensure `sender_address` is a single email matching the SMTP login user. If it contains comma-separated emails, remove extras. The notification's `email` field handles multiple recipients — `sender_address` is the `From:` header only.
|
||||
|
||||
```sql
|
||||
UPDATE wp_posts
|
||||
SET post_content = REPLACE(post_content,
|
||||
'sender_address\":\"contact@site.com, admin@site.com',
|
||||
'sender_address\":\"contact@site.com')
|
||||
WHERE ID IN (<form_ids>);
|
||||
```
|
||||
|
||||
### 3. Diagnostic Script
|
||||
|
||||
```bash
|
||||
# Check SMTP connectivity from WordPress host
|
||||
echo | openssl s_client -connect <smtp_host>:2525 -starttls smtp 2>&1 | grep -c 'CONNECTED'
|
||||
|
||||
# Test SMTP login
|
||||
python3 -c "
|
||||
import smtplib, ssl
|
||||
ctx = ssl.create_default_context()
|
||||
with smtplib.SMTP('<host>', 2525, timeout=15) as s:
|
||||
s.starttls(context=ctx)
|
||||
s.login('<user>', '<pass>')
|
||||
msg = 'From: <from>\\nTo: <to>\\nSubject: Test\\n\\nBody'
|
||||
s.sendmail('<from>', ['<to>'], msg)
|
||||
print('OK')
|
||||
"
|
||||
|
||||
# Check WP Mail SMTP debug events for recent failures
|
||||
mysql -u <db_user> -p'<db_pass>' <db_name> -e "
|
||||
SELECT COUNT(*) FROM wp_wpmailsmtp_debug_events
|
||||
WHERE event_type = 0 AND created_at >= NOW() - INTERVAL 10 MINUTE;
|
||||
"
|
||||
|
||||
# Full debug log for recent errors
|
||||
mysql -u <db_user> -p'<db_pass>' <db_name> -e "
|
||||
SELECT id, content FROM wp_wpmailsmtp_debug_events
|
||||
WHERE event_type = 0 ORDER BY id DESC LIMIT 3\G
|
||||
"
|
||||
```
|
||||
|
||||
### 4. Watchdog Pattern (every 5 min)
|
||||
|
||||
The `apex-mail-watchdog.sh` script runs as a no_agent cron on Core, SSHing into the WordPress host to:
|
||||
|
||||
1. Send a test email via Python SMTP (silent on success)
|
||||
2. Check `wp_wpmailsmtp_debug_events` for recent failures (silent on success)
|
||||
3. On failure: alerts the user via Hermes cron's delivery mechanism
|
||||
|
||||
**Key detail for the watchdog:** on success, the script exits 0 with NO stdout. Only failures produce output. This keeps the user's chat clean.
|
||||
Reference in New Issue
Block a user