Make WordPress Core

Opened 3 hours ago

Last modified 2 hours ago

#66298 new defect (bug)

wp_set_object_terms() leaves term_order at 0 for newly added terms in sorted taxonomies when term counting is deferred

Reported by: jdeep Owned by:
Priority: normal Milestone: Awaiting Review
Component: Taxonomy Version:
Severity: normal Keywords: has-patch has-unit-tests
Cc: Focuses:

Description (last modified by jdeep)

For a taxonomy registered with 'sort' => true, wp_set_object_terms() is meant to store terms in the order they are passed. When term counting is deferred and a term is added to an object that already has terms, the new term is left at the column default term_order = 0. It then sorts ahead of the existing terms instead of after them.

The wrong value is written to wp_term_relationships so the order of terms is destroyed.

Steps to reproduce

Add this MU plugin to a site. It registers a sorted taxonomy and defers term counting.

<?php
/**
 * Plugin Name: Term order bug reproduction
 * Description: Registers a sorted taxonomy and defers term counting, to reproduce the `term_order` bug in `wp_set_object_terms()`.
 */

add_action(
        'init',
        static function () {
                register_taxonomy(
                        'byline',
                        'post',
                        array(
                                'label'        => 'Byline Author',
                                'sort'         => true,
                                'args'         => array( 'orderby' => 'term_order' ),
                                'show_in_rest' => true,
                        )
                );

                wp_defer_term_counting( true );
        }
);

Then, in wp-admin:

  1. Go to Posts > Byline Author and create three terms: Alice, Bob and Charlie.
  2. Create a new post.
  3. From the Quick Edit panel, add Alice, then Bob to Byline Authors and save the post.
  4. Add Charlie in the same panel, after Bob. Save the post again.
  5. Check the Byline Authors again from the Quick Edit Panel

Result: Chalie, Alice, Bob

Expected: Alice, Bob, Charlie

See Video: ​https://github.com/user-attachments/assets/9c91783e-ecd6-4201-84de-e99e2bf08819

For sorted taxonomies, wp_set_object_terms() reads the object's terms again before it assigns term_order, and only numbers the terms it finds in that read.

That read is cached. Normally the term count update clears the cache before the read happens. When term counting is deferred, nothing clears it, so the read returns the old list without the newly added term. The new term is skipped and keeps term_order = 0.

wp_set_object_terms() does clear the cache itself, but only after this read. That was added in #40306.

We hit this during a bulk content migration. We turned off term counting for the whole run with wp_defer_term_counting( true ) to improve migration speed. Byline authors were stored in a sorted taxonomy, and on posts with more than one author the byline order came out wrong.

Change History (2)

This ticket was mentioned in ​PR #14242 on ​WordPress/wordpress-develop by ​@jdeep.


3 hours ago
#1

  • Keywords has-patch has-unit-tests added

For taxonomies registered with 'sort' => true, wp_set_object_terms() re-reads the object's terms before it assigns term_order. That read is cached, and when term counting is deferred nothing invalidates the cache after the new relationships are inserted. The newly added term is missing from the read, is skipped, and keeps term_order = 0.

This invalidates the term query cache right after new relationships are added, so the read always includes them. When counting is not deferred, the count update already does this.

Adds two tests: one where the cache is primed by an earlier wp_get_object_terms() call, and one where the taxonomy's args order by term_order, which makes wp_set_object_terms() prime the cache itself.

Bug Reproduction:

  1. Add this MU plugin to a site. It registers a sorted taxonomy and defers term counting.
    <?php
    /**
     * Plugin Name: Term order bug reproduction
     * Description: Registers a sorted taxonomy and defers term counting, to reproduce the `term_order` bug in `wp_set_object_terms()`.
     */
    
    add_action(
            'init',
            static function () {
                    register_taxonomy(
                            'byline',
                            'post',
                            array(
                                    'label'        => 'Byline Author',
                                    'sort'         => true,
                                    'args'         => array( 'orderby' => 'term_order' ),
                                    'show_in_rest' => true,
                            )
                    );
    
                    wp_defer_term_counting( true );
            }
    );
    
  2. Then, in wp-admin:
    • Go to Posts > Byline Author and create three terms: Alice, Bob and Charlie.
    • Create a new post.
    • From the Quick Edit panel, add Alice, then Bob to Byline Authors and save the post.
    • Add Charlie in the same panel, after Bob. Save the post again.
    • Check the Byline Authors again from the Quick Edit Panel

Result: Chalie, Alice, Bob
Expected: Alice, Bob, Charlie

​https://github.com/user-attachments/assets/9c91783e-ecd6-4201-84de-e99e2bf08819

AI assistance: Yes
Tool(s): Claude Code
Model(s): Opus 5.5
Used for: Debugging the issue and drafted the patch and unit tests. I reviewed the implementation and the code changes and ran the new tests.

#2 @jdeep
2 hours ago

  • Description modified (diff)
Note: See TracTickets for help on using tickets.