Skip to content

Guidelines: Fix fatal when rest_api_init fires before init#78350

Merged
mikachan merged 2 commits into
WordPress:trunkfrom
xavier-lc:fix/guidelines-rest-api-init-ordering
May 15, 2026
Merged

Guidelines: Fix fatal when rest_api_init fires before init#78350
mikachan merged 2 commits into
WordPress:trunkfrom
xavier-lc:fix/guidelines-rest-api-init-ordering

Conversation

@xavier-lc

Copy link
Copy Markdown
Contributor

What?

Ensures the wp_guideline custom post type is registered before any rest_api_init callback runs, so the Guidelines feature does not fatal when rest_api_init fires before init priority 10.

Why?

Gutenberg_Guidelines_Post_Type::register() is hooked to init at priority 10. The rest_api_init callbacks in lib/experimental/guidelines/index.php then assume the post type exists:

  • Gutenberg_Content_Guidelines_REST_Controller and Gutenberg_Content_Guidelines_Revisions_Controller are instantiated via parent::__construct( 'wp_guideline' ), where the WP core parent constructors dereference get_post_type_object( $parent_post_type ) without a null check (WP_REST_Posts_Controller::__construct, WP_REST_Revisions_Controller::__construct).
  • Gutenberg_Guidelines_Post_Type::register_post_meta() registers meta with revisions support, which _doing_it_wrongs if the parent post type does not yet declare revisions support.

init normally fires before rest_api_init, but anything that calls rest_get_server() early — for example a host plugin that needs an internal REST dispatch during plugins_loaded — boots the REST server, which fires rest_api_init immediately. In that window the post type is not yet registered and the callbacks above fatal.

This was observed in production on WordPress.com when activating the Gutenberg gutenberg-guidelines experiment alongside hosting code that boots the REST server during plugins_loaded:

Fatal error: Uncaught Error: Call to a member function get_rest_controller() on null
  in wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php:61

How?

Adds a single priority-1 rest_api_init callback that registers the post type if it does not already exist:

add_action(
    'rest_api_init',
    static function () {
        if ( ! post_type_exists( Gutenberg_Guidelines_Post_Type::POST_TYPE ) ) {
            Gutenberg_Guidelines_Post_Type::register();
        }
    },
    1
);

Running at priority 1 means every subsequent rest_api_init callback — including register_post_meta and the controller instantiations — sees a fully-registered post type regardless of how the REST server was booted. The normal init priority-10 registration remains in place; this is purely a defensive guard for the early-boot path.

## Testing Instructions

npm run test:unit:php:base -- --filter test_rest_api_init_does_not_fatal_when_post_type_not_yet_registered

The new test (phpunit/experimental/guidelines/class-gutenberg-content-guidelines-revisions-controller-test.php) unregisters the post type and fires rest_api_init directly, simulating the ordering scenario. Without the fix it errors on the null post type object; with the fix it passes.

Xavier Lozano Carreras added 2 commits May 15, 2026 15:43
Hosts can call rest_get_server() during plugins_loaded, which fires rest_api_init before init priority 10 has had a chance to register the wp_guideline post type. The rest_api_init callbacks in lib/experimental/guidelines/index.php dereference the post type object, so without a defensive guard they fatal (controllers) or trip _doing_it_wrong (register_post_meta).

This test simulates that ordering by unregistering the post type and firing rest_api_init directly.
…acks

init normally fires before rest_api_init, but anything that calls rest_get_server() early (e.g. from plugins_loaded) fires rest_api_init before init priority 10 has had a chance to register the wp_guideline post type. When that happens, the rest_api_init callbacks dereference a null post type object:
  - Gutenberg_Content_Guidelines_REST_Controller and Gutenberg_Content_Guidelines_Revisions_Controller fatal in their parent constructors (WP_REST_Posts_Controller::__construct and WP_REST_Revisions_Controller::__construct).
  - Gutenberg_Guidelines_Post_Type::register_post_meta trips a _doing_it_wrong notice about meta keys enabling revisions support on a subtype that does not support revisions.
Add a single priority-1 rest_api_init guard that registers the post type if it does not yet exist, so all subsequent rest_api_init callbacks see a fully-registered post type regardless of how the REST server was booted.
@xavier-lc xavier-lc requested a review from spacedmonkey as a code owner May 15, 2026 13:50
@github-actions

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.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @xavier.lozano.carreras@a8c.com.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

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

Unlinked contributors: xavier.lozano.carreras@a8c.com.


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

@mikachan mikachan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @xavier-lc!

@mikachan mikachan added [Type] Bug An existing feature does not function as intended [Feature] Guidelines An experimental feature for adding site-wide editorial rules. Backport to Gutenberg RC Pull request that needs to be backported to a Gutenberg release candidate (RC) labels May 15, 2026
@mikachan mikachan merged commit 2e4b593 into WordPress:trunk May 15, 2026
53 of 54 checks passed
@github-actions github-actions Bot added this to the Gutenberg 23.3 milestone May 15, 2026
@paulopmt1 paulopmt1 added the Backport to Gutenberg Minor Release Pull request that needs to be backported to a Gutenberg minor release label May 20, 2026
@paulopmt1 paulopmt1 added the Backport to WP 7.0 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label May 20, 2026
@github-actions

Copy link
Copy Markdown

There was a conflict while trying to cherry-pick the commit to the wp/7.0 branch. Please resolve the conflict manually and create a PR to the wp/7.0 branch.

PRs to wp/7.0 are similar to PRs to trunk, but you should base your PR on the wp/7.0 branch instead of trunk.

# Checkout the wp/7.0 branch instead of trunk.
git checkout wp/7.0

# Create a new branch for your PR.
git checkout -b my-branch

# Cherry-pick the commit.
git cherry-pick 2e4b59303c5194d3810721b341be2bc76bddddef

# Check which files have conflicts.
git status

# Resolve the conflict...
# Add the resolved files to the staging area.
git status
git add .
git cherry-pick --continue

# Push the branch to the repository
git push origin my-branch

# Create a PR and set the base to the wp/7.0 branch.
# See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request.

@paulopmt1 paulopmt1 removed Backport to WP 7.0 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta Backport to Gutenberg RC Pull request that needs to be backported to a Gutenberg release candidate (RC) labels May 20, 2026
mikachan added a commit that referenced this pull request May 20, 2026
…pick #78350) (#78477)

* Guidelines: Add test for rest_api_init firing before init

Hosts can call rest_get_server() during plugins_loaded, which fires rest_api_init before init priority 10 has had a chance to register the wp_guideline post type. The rest_api_init callbacks in lib/experimental/guidelines/index.php dereference the post type object, so without a defensive guard they fatal (controllers) or trip _doing_it_wrong (register_post_meta).

This test simulates that ordering by unregistering the post type and firing rest_api_init directly.

* Guidelines: Ensure post type is registered before rest_api_init callbacks

init normally fires before rest_api_init, but anything that calls rest_get_server() early (e.g. from plugins_loaded) fires rest_api_init before init priority 10 has had a chance to register the wp_guideline post type. When that happens, the rest_api_init callbacks dereference a null post type object:
  - Gutenberg_Content_Guidelines_REST_Controller and Gutenberg_Content_Guidelines_Revisions_Controller fatal in their parent constructors (WP_REST_Posts_Controller::__construct and WP_REST_Revisions_Controller::__construct).
  - Gutenberg_Guidelines_Post_Type::register_post_meta trips a _doing_it_wrong notice about meta keys enabling revisions support on a subtype that does not support revisions.
Add a single priority-1 rest_api_init guard that registers the post type if it does not yet exist, so all subsequent rest_api_init callbacks see a fully-registered post type regardless of how the REST server was booted.

---------

Co-authored-by: Xavier Lozano Carreras <8511199+xavier-lc@users.noreply.github.com>
Co-authored-by: Xavier Lozano Carreras <xavier.lozano.carreras@a8c.com>
Co-authored-by: xavier-lc <xavilc@git.wordpress.org>
Co-authored-by: mikachan <mikachan@git.wordpress.org>
@paulopmt1 paulopmt1 removed the Backport to Gutenberg Minor Release Pull request that needs to be backported to a Gutenberg minor release label May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Guidelines An experimental feature for adding site-wide editorial rules. [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants