Skip to main content

Tutorial

WP_DEBUG every form submission

Most form-related bugs are silent failures. Turning on WP_DEBUG plus Core Forms' email log catches them in 30 seconds. Here's the setup.

Half the form-related support tickets I get are silent failures. The submission goes through. The success message shows. But the email never lands, the Mailchimp action didn’t fire, the Slack notification got swallowed by a network error.

Without logs, debugging this is impossible. With logs, it takes 30 seconds.

Here’s the debug setup I run on every WordPress site I maintain.

Step 1: Turn on WP_DEBUG (correctly)

Most “WP_DEBUG how to” guides tell you to add define('WP_DEBUG', true) to wp-config.php and call it done. That’s wrong, because by default it prints errors to the screen, breaking your site visually.

The right setup:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

WP_DEBUG_LOG writes errors to /wp-content/debug.log. WP_DEBUG_DISPLAY keeps them off the rendered page.

Now your error log has every PHP notice, warning, and fatal. None of it is visible to visitors.

Step 2: Tail the log

In a separate terminal, on the server:

tail -f wp-content/debug.log

Submit a form. Watch what shows up.

If a third-party API fails (Mailchimp, HubSpot, Slack webhook), Core Forms logs the error to debug.log automatically. You’ll see something like:

[09-May-2026 14:33:12 UTC] Core Forms Mailchimp action failed: Invalid API key

That’s the exact answer to “why isn’t the lead landing in Mailchimp?”

Step 3: Use Core Forms’ email log

For email-specific failures, the email log is faster than debug.log.

Settings → Email logs → On. Now every email Core Forms sends is logged with its status: sent, queued, failed, bounced.

When a client says “I’m not getting form notifications,” the email log answers in two clicks:

  • Status “sent” → email left the server, deliverability problem. (See the deliverability piece.)
  • Status “failed” → SMTP refused the connection. Check credentials.
  • No log entry at all → the form action didn’t fire. Check the form’s Actions tab.

Each of those is a different fix. The log tells you which to chase.

Step 4: Use Query Monitor for the rest

For everything debug.log doesn’t show — slow database queries, hooks that fired, scripts enqueued, HTTP requests, transient activity — use Query Monitor. It’s free.

Activate it. Submit a form. Open the Query Monitor admin bar. Click “HTTP API Calls.”

You’ll see every external request the form action made. The URL it hit. The response status. The response body. The duration.

For “Mailchimp didn’t subscribe me,” this is the answer:

POST https://us20.api.mailchimp.com/3.0/lists/abc/members
→ Status 401 Unauthorized
→ Body: {"detail": "Invalid API key"}

Six seconds from “the form is broken” to “the API key is wrong.”

Step 5: Add custom logging if you need it

If you’ve written a custom hook on cf_form_success, add error_log() calls inside it:

add_action( 'cf_form_success', function( $submission, $form ) {
    error_log( 'cf_form_success: form ' . $form->slug . ', email ' . ($submission->data['email'] ?? 'none') );
    // ... your logic
}, 10, 2 );

Now tail -f debug.log shows every successful submission. Your custom code is no longer a black box.

What this catches in practice

Three real bugs I’ve debugged with this setup:

1. Mailchimp API key expired. Client rotated their Mailchimp keys two months ago, never updated the form. Email log was empty for Mailchimp. debug.log showed “Invalid API key” on every submission. Fix: paste the new key. Time to diagnose: 30 seconds.

2. Postmark account suspended. Postmark suspended the account for high bounce rate. SMTP started rejecting. Email log showed “failed” on every email for two weeks. The client hadn’t noticed because they assumed leads had stopped coming in. Time to diagnose: 30 seconds. Time to fix: an hour with Postmark support.

3. Notion property mismatch. Form pushed to a Notion database where someone had renamed the “Email” column to “Contact email.” The action silently dropped the email field. debug.log showed Notion’s API error: “Property ‘Email’ not found.” Fix: update the field mapping in the form’s Notion action. Time to diagnose: 30 seconds.

Without the log, each of these would have been an hour-plus support call.

What you should not do

Don’t enable WP_DEBUG on production with WP_DEBUG_DISPLAY=true. It dumps errors into the rendered page. Your site looks broken to anyone who triggers a notice.

Don’t leave WP_DEBUG_LOG writing forever without rotating. The log file grows. After three months, it’s 200MB. Your daily cron should rotate it. (cp debug.log debug.log.old && >debug.log does the trick.)

Don’t skip the email log because “I have SMTP plugin logs already.” The Core Forms email log records form-specific context (which form, which submission, which action). SMTP plugin logs only know “an email tried to send.” The forms-side context is what makes debugging fast.

The thirty-second test

On any WordPress site running Core Forms:

1. Confirm wp-config.php has WP_DEBUG_LOG=true and WP_DEBUG_DISPLAY=false.
2. Confirm Settings → Email logs is on.
3. Submit a test form.
4. Check debug.log and the email log.

If both are quiet on a working submission, your debug pipeline is wired up. The next bug shows up in the logs by default.

The next step

Add the four define() lines to wp-config.php on one client site. Turn on the email log. The first time you save 90 minutes on a “form is broken” call, this stops being a habit and becomes a setup default.

Email logs are bundled in Core Forms. Pricing.

Build the form. Stop reading.

Every note here came out of a real Core Forms setup. Use CFLAUNCH for 20% off either plan.