In short, your code is fine, but the custom query’s pagination and the one for the main query, they can’t be using the same paged
URL query string (which WordPress reads the value from for the paged
query arg used in a WP_Query
request).
Why the error 404?
So for examples, paginated/paged category requests can have one of these URLs:
-
Full pretty URL:
https://example.com/category/uncategorized/page/2/
-
Semi-pretty URL which redirects to the above URL:
https://example.com/category/uncategorized/?paged=2
-
“Ugly URL” (e.g. when permalinks are not enabled)
https://example.com/?cat=1&paged=2
And as you can see, the URLs are using the query string paged
which you can get the value using get_query_var( 'paged' )
. (Note that in the first URL, the page/2
is equivalent to paged=2
)
And because a category/taxonomy archive is a non-singular request (just like search results pages), the paged
value will be used in the main query’s request, i.e. the SQL command for querying the requested archive, unless of course if pagination is not enabled for the main query.
Therefore, the paged
value needs to be valid, i.e. it must not exceed the max number of pages for that specific request (the main query). And if the max is exceeded, then you’d get a 404
error because there were no more posts found via the main query. Just like a book with 10 pages (including covers); there’s logically no page #11, right?
So because your custom query is being paginated using the paged
query string, then if you get the error 404, it’s likely because the custom query had more pages than the main query.
Sample scenario demonstrating the above:
-
On a category archive page (e.g. at
example.com/category/uncategorized
), there were 3 pages shown in the pagination. And that’s for the main query which WordPress runs first on page load. -
Then in the category template, you run a custom query for, maybe a custom post type.
-
Then you added a pagination for the custom query and there were 5 pages shown in that pagination.
-
So if both the paginations are using the same
paged
query string, then for example, going to page #4 will cause a404
error because the main query (for the category) had only at most 3 pages of results.
How to fix the error
(Note: These are listed in no specific order.)
-
As I said in the comments, you can use a custom URL query string like
page_num
,pg
, etc. along withpaged
.So the
paged
will always be just for the main query, whilepage_num
orpg
will be used with your custom query. -
Use AJAX instead to paginate the custom query, but AJAX is not in scope of this answer. (Or that it’s up to you to look for a solution and implement it.)
-
Use a static Page (post of the
page
type), e.g. atexample.com/my-tax-archive
, and run your custom queries in the Page Template.For singular requests, WordPress doesn’t use the
paged
in the request SQL, so thepaged
value can be any number (2, 20, 200, 2000, etc.).
But there’s a trick to make paged
works for both the main query and custom queries.. on archive pages.
-
Add this to your theme’s
functions.php
file:add_action( 'pre_get_posts', function ( $query ) { if ( is_admin() || ! $query->is_main_query() || ! is_tax( 'your_tax' ) ) { return; } if ( ! empty( $_GET['pgs'] ) && ( $paged = max( 1, $query->get( 'paged' ) ) ) && // Runs only if the current page number exceeds the main query's max pages. $paged > $_GET['pgs'] ) { $query->set( 'pg', $paged ); // for the custom query's pagination $query->set( 'paged', $_GET['pgs'] ); // for the main query's pagination // Prevent WordPress from redirecting to page/<max pages>. remove_action( 'template_redirect', 'redirect_canonical' ); } else { $query->set( 'pg', $query->get( 'paged' ) ); } } );
-
Then in the
bootstrap_pagination()
function, apply the four ([1]
to[4]
) changes below:// [1] Add the $use_alt function bootstrap_pagination( \WP_Query $wp_query = NULL, $echo = TRUE, $use_alt = null ) { ... // [2] Add these: $add_args = []; if ( $use_alt ) { $add_args['pgs'] = $GLOBALS['wp_query']->max_num_pages; } $pages = paginate_links( [ ... // [3] Use this instead. 'current' => max( 1, get_query_var( $use_alt ? 'pg' : 'paged' ) ), ... // [4] Use the $add_args 'add_args' => $add_args, ... ] ); ... }
-
Then in the archive/
taxonomy.php
template:// Define the $paged like so: $paged = max( 1, get_query_var( 'pg' ) ); // ... your code here. // Then call bootstrap_pagination() like so: bootstrap_pagination( $the_query, true, true );
Tried & tested working, but obviously as you can see above, once the max num pages for the main query has been reached, the next pages will set the paged
to that max num pages value. So it’s up to you how to not make that “look bad/weird/whatever” and to handle things like performance (if your custom query had 20 extra pages than the main query, then one would see the main query’s last page 20 times.. if they viewed them all).