Skip to content

fix: prevent error on general settings save - #1080

Merged
dkotter merged 5 commits into
WordPress:developfrom
Pranjal1423:fix/1048-settings-admin-email-save
Oct 5, 2026
Merged

dkotter merged 5 commits into
WordPress:developfrom
Pranjal1423:fix/1048-settings-admin-email-save

Conversation

@Pranjal1423

@Pranjal1423 Pranjal1423 commented Sep 30, 2026 •

Copy link
Copy Markdown

What?

Closes #1048

This PR prevents options.php form validation errors on Settings › General saves by preserving and restoring $GLOBALS['new_allowed_options'] when register_initial_settings() is called inside Settings::register().

Why?

Since 1.2.0 (#856), Settings::register() calls register_initial_settings() when abilities initialize before rest_api_init, ensuring core settings exist in $wp_registered_settings before taking the snapshot for core/read-settings.

However, register_initial_settings() has a side effect: it calls register_setting( 'general', 'admin_email' ), which appends 'admin_email' to $new_allowed_options['general'].

On ordinary admin form POSTs to wp-admin/options.php, WordPress iterates over every allowed option in the group and updates it. Because the Settings › General form posts new_admin_email instead of admin_email, options.php receives null for admin_email. In turn, sanitize_option( 'admin_email', null ) / is_email( null ) triggers PHP deprecation notices (preg_match / strlen parameter null) and an invalid email error notice ("The email address isn't correct"), even though the form is completely valid.

How?

  1. In includes/Abilities/Settings/Settings.php, captured $GLOBALS['new_allowed_options'] immediately prior to calling register_initial_settings().
  2. Restored $GLOBALS['new_allowed_options'] (and re-linked the legacy alias $GLOBALS['new_whitelist_options']) immediately afterwards. This keeps $wp_registered_settings populated for the Abilities registry without leaking admin_email into form handling.
  3. Added an integration test test_register_preserves_new_allowed_options() in tests/Integration/Includes/Abilities/Settings/SettingsTest.php to verify that admin_email is not added to $new_allowed_options['general'] and existing allowed options are preserved.

Use of AI Tools

AI assistance: Yes
Tool(s): Antigravity
Used for: Root-cause debugging, patch implementation, and integration test creation; final implementation and tests were tested and reviewed by me.

Testing Instructions

  1. Ensure the Custom Abilities experiment is enabled under Settings › AI › Experiments (or an mu-plugin with add_action( 'admin_init', function() { wp_get_abilities(); } ); is active).
  2. Navigate to Settings › General (/wp-admin/options-general.php).
  3. Scroll to the bottom and click Save Changes without making modifications.
  4. Verify the page reloads cleanly with the green "Settings saved." notice, with no deprecation notices or email validation error banners.
  5. Verify the PHPUnit integration tests pass:
    npx wp-env run cli vendor/bin/phpunit -c phpunit.xml.dist tests/Integration/Includes/Abilities/Settings/SettingsTest.php

After Fix

Screenshot 2026-09-30 at 6 55 07 PM

Changelog Entry

Fixed - Prevent options.php validation error when saving general settings with abilities active.

Open WordPress Playground Preview

Preserve $new_allowed_options when register_initial_settings() is called early in Settings::register() so 'admin_email' is not added to the allowed options list on admin form saves.

Fixes WordPress#1048
@Pranjal1423
Pranjal1423 requested a review from a team September 30, 2026 13:43
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: Pranjal1423 <pranjal4804@git.wordpress.org>
Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: juanlentino <juanml@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov

codecov Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.60%. Comparing base (aae6de2) to head (52d72e7).

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #1080   +/-   ##
==========================================
  Coverage      81.59%   81.60%           
  Complexity      3081     3081           
==========================================
  Files            129      129           
  Lines          12281    12283    +2     
==========================================
+ Hits           10021    10023    +2     
  Misses          2260     2260           
Flag Coverage Δ
unit 81.60% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dkotter dkotter added this to the 1.4.0 milestone Oct 5, 2026
@dkotter
dkotter merged commit dd72cbd into WordPress:develop Oct 5, 2026
35 of 36 checks passed
jorgefilipecosta added a commit to jorgefilipecosta/wordpress-develop that referenced this pull request Oct 6, 2026
Ports WordPress/ai#1080. When the abilities
registry initializes before `rest_api_init`, register() calls
register_initial_settings(), which also adds core's settings to
$new_allowed_options. On an options.php request, those settings are then
processed even though the submitted form does not include them (for
example admin_email on Settings > General), which triggers a validation
error. Restore $new_allowed_options after the early registration.
jorgefilipecosta added a commit to jorgefilipecosta/wordpress-develop that referenced this pull request Oct 6, 2026
Ports WordPress/ai#1080. When the abilities
registry initializes before `rest_api_init`, register() calls
register_initial_settings(), which also adds core's settings to
$new_allowed_options. On an options.php request, those settings are then
processed even though the submitted form does not include them (for
example admin_email on Settings > General), which triggers a validation
error. Restore $new_allowed_options after the early registration.
jorgefilipecosta added a commit to jorgefilipecosta/wordpress-develop that referenced this pull request Oct 6, 2026
Abilities can initialize before or without `rest_api_init`, where the
initial settings are registered, for example on cron or WP-CLI. So far
`WP_Abilities_Settings::register()` registered them itself. Register them
from a `wp_abilities_api_init` callback at priority 1 instead, before the
core abilities, so the settings ability no longer handles the settings
bootstrap.

The callback keeps the existing checks: it does nothing once the REST API
has registered the settings, and it restores `$new_allowed_options`, so
saving Settings > General does not try to save `admin_email` (see
WordPress/ai#1080).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants