Custom Post Types, Page Templates and Pagination. Why do I get a 404 Error?

I have spent hours today on a maddening issue I was having with displaying a custom post type. Everything displayed beautifully, except the pagination simply would not work properly.

I have a page-template setup to display a “page of posts” (see Codex: Page of Posts) for a custom post type (‘news’).

Read More

I keep getting a 404 error when I click “Older Posts”. I noticed a lot of other people have or had this problem.

Why is this happening? Here is my query & loop code:

<?php 
    // Enable Pagination
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

    // The Args
    $args= array(
        'post_type' => 'news',
        'paged' => $paged,
        );

    // The Query
    query_posts($args); 

    // The Loop
    while ( have_posts() ) : the_post(); ?>
// ...

Related posts

Leave a Reply

3 comments

  1. I figured this problem out literally seconds before I posted the question, so I thought I would go ahead and post the answer here. I’ve noticed there is not a lot of consolidated help for this problem out there.

    This will help you if your “Older Posts” or “Newer Posts” links give you a 404 error when trying to display custom posts on a page-template.

    Here are the steps to solve the 404 issue (in most cases) if you are having it. Check after each to see if the problem goes away.

    Step 1: Flush your permalinks

    Go to settings => permalinks. Select default. Save. Test. Select your preference. Save. Test again.

    Step 2: Check your query.

    If flushing the permalinks doesn’t fix the problem, double check your query. Use the Codex (see: Pages > page of posts). You need to define the $paged variable and use it in your query options.

    Step 3: Make sure your posts_per_page does not conflict with the default.

    There are some folks discussing this as a bug in the wordpress core. Whether or not that is the case, the posts_per_page => x option can cause issues with pagination if x is less than the default option.

    A hack-ish solution to this issue is to set the default to 1 (settings > reading > “blogs show at most _ pages”).

    NOTE: At this point, I was STILL having the problem.. it was step 4 that finally fixed it for me.

    Step 4: Make sure your page slug != your post_type.

    In my case, I had post_type => 'news'. And the page I was using was http://www.mysite.com/news. This caused the conflict that ultimately goofed the pagination up. To fix, I changed my post_type to ‘news-articles’ and re-flushed the permalinks. The problem went away.

    Hopefully this helps you if you experience the same issue. I have a feeling a lot of people fall into this snare – it’s easy to do, and it’s not super intuitive to figure out. (at least it wasn’t for me).

  2. About Step 3. Here is explanation why it happens: “Pagination is calculated before you get to the template file that runs query_posts. The proper way to alter posts_per_page conditionally is to use the pre_get_posts hook to modify the main query.”
    https://wordpress.stackexchange.com/a/42063/17177
    I found one implementation “pre_get_posts” that works for me http://uncommoncontent.com/2012/01/28/add-custom-post-types-to-the-loop:

    if ( ! function_exists( 'ucc_add_cpts_to_pre_get_posts' ) ) {
        function ucc_add_cpts_to_pre_get_posts( $query ) {
            if ( $query->is_main_query() && ! is_post_type_archive() && ! is_singular() && ! is_404() ) {
            $my_post_type = get_query_var( 'post_type' );
                if ( empty( $my_post_type ) ) {
                    $args = array(
                        'exclude_from_search' => false,
                        'public' => true,
                        '_builtin' => false
                    );
                    $output = 'names';
                    $operator = 'and';
                    $post_types = get_post_types( $args, $output, $operator );
    
                    // Or uncomment and edit to explicitly state which post types you want.
                    // $post_types = array( 'event', 'location' );
                    // Add 'link' and/or 'page' to array() if you want these included.
                    // array( 'post', 'link', 'page' ), etc.
                    $post_types = array_merge( $post_types, array( 'post' ) );
                    $query->set('post_type', $post_types );
                }
            }
        } 
    }
    
    add_action( 'pre_get_posts', 'ucc_add_cpts_to_pre_get_posts' );