Opened 4 weeks ago
Closed 4 weeks ago
#65868 closed defect (bug) (duplicate)
Sticky posts appear twice: at the top of page 1 and again in date order on a later page
| Reported by: | sanket.parmar | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | Query | Version: | 7.1 |
| Severity: | normal | Keywords: | |
| Cc: | Focuses: |
Description
When a sticky post is old enough to fall on a later page of a paginated posts index, it is rendered twice: once pinned at the top of page 1, and again in its natural chronological position on the later page.
Steps to reproduce
On a clean install with the default block theme (Twenty Twenty-Five) and the posts index as the front page:
wp option update posts_per_page 5
# 12 posts, one per day going backwards
wp eval 'for ($i = 1; $i <= 12; $i++) {
$d = date("Y-m-d H:i:s", strtotime("-{$i} days 10:00"));
wp_insert_post([
"post_title" => "QL test post {$i}",
"post_status" => "publish",
"post_type" => "post",
"post_date" => $d,
]);
}'
# make an older post sticky - one that naturally falls on page 3
wp option update sticky_posts '[<ID of QL test post 10>]' --format=json
Then view /, /page/2/ and /page/3/.
Expected
The sticky post is shown once, pinned to the top of page 1.
Actual
With 15 published posts at 5 per page and "QL test post 10" (dated August 3) sticky.
Page 1 - the sticky post is pinned above posts dated August 12, 11 and 10, which is correct. Note also that six posts are rendered on a five-per-page setting:
Page 3 - the same post is rendered a second time, in its natural date position between "QL test post 9" and "QL test post 11":
Not specific to the block editor
This was first reported against the Gutenberg Query Loop block. It is not specific to that block. With every plugin deactivated, including Gutenberg:
page 3 still renders the duplicate:
The theme's Query Loop uses "inherit":true, so the posts come from the main query rather than from any block-specific query code.
Where it comes from
In wp-includes/class-wp-query.php, sticky handling is gated on the first page only:
$sticky_posts = get_option( 'sticky_posts' );
if ( $this->is_home && $page <= 1 && is_array( $sticky_posts ) && ! empty( $sticky_posts ) && ! $query_vars['ignore_sticky_posts'] ) {
That block reorders the current page's results in memory and prepends any sticky post that was not already among them. Nothing removes sticky posts from the queries that run for pages 2 and above, so the post keeps its slot in normal date-ordered pagination and is rendered a second time.
This also accounts for page 1 returning one more item than posts_per_page when the sticky post was not already in that page's result set.
Question
Is the duplication intended, or should sticky posts be excluded from the result set on subsequent pages? The $page <= 1 gate looks deliberate, so this may be long-standing accepted behaviour rather than a regression, in which case documenting it would help.
Environment
- WordPress 7.0.4
- Theme: Twenty Twenty-Five (block theme, posts index as front page)
- All plugins deactivated for the screenshots above
- PHP 8.3, MariaDB 11.8
Notes
Originally reported at https://github.com/WordPress/gutenberg/issues/80624, where the reporters describe this as a change between 7.0.1 and 7.0.2. I was not able to verify that; I tested only 7.0.4, and the $page <= 1 gate above suggests the behaviour may predate it. I also tested only a block theme, not a classic theme.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)




Confirming the report on current trunk, and adding some ticket history, since the
$page <= 1gate is deliberate and this has come up several times before.Reproduced on trunk with 23 posts,
posts_per_page10, and stickies at fixture index 2, 14 and 8:Both symptoms hold: page 1 returns
posts_per_page + 1, and a sticky whose natural date position falls on a later page is rendered a second time there.Prior art
The gate dates back to the original sticky posts implementation in WordPress 2.7 (#7457), and the trade-off has been argued before:
posts_per_page + 1when a sticky falls outside the page. Closed wontfix. In comment 5, Denis-de-Bernardy sets out the cost: stickies would have to be resolved first and theLIMITadjusted, and "we could end up stripping stickies from all non singular queries where they might appear, but the performance impact is not benign".2nd opinion. bendoh attachedtrac-27872-on-31418.diff, which folds sticky handling into the SQL query, and notes "paging will work correctly with any number of sticky posts". That patch addresses exactly the duplication reported here and has not been reviewed.An existing test overstates its coverage
tests/phpunit/tests/query/stickies.phpalready has a test that reads as though it covers this:self::$posts[2]is the third-newest post in that fixture, so it sits on page 1 naturally and could never appear on page 2 — the assertion passes either way. The sticky that does land on page 2 in the same fixture,self::$posts[14], is never checked. The name claims coverage the test does not provide, which may be part of why this went unnoticed.Question
Is this worth pursuing on its own, or should it be closed as a duplicate of #27282? I have not attached a patch: excluding stickies from later pages moves
found_posts,max_num_pagesand the composition of every page of the main home query on every site, and that decision looks like it belongs with #27282 and the wontfix on #9300 rather than being settled here.Use of AI Tools
AI assistance: Yes
Used for: reproducing the report against trunk, and researching the related tickets and test coverage cited above. All findings were reviewed and verified by me.