Why do I get a 404 error on my custom post type archive pagination?

Why Does It Happen?

It’s because the number of pages in your second WP_Query is large the number of pages in the main query.

So if there are 8 jobs, that’s 4 pages in your custom query, and 1 page in the main query. So when you go to page 2 there is no page 2 in the main query resulting in a 404. This isn’t a problem when using pre_get_posts which modifies the main query instead of creating a second one.

A Solution

If you remove the custom WP_Query you can revert to a standard main post loop, and standard pagination.

Then, to enforce the 2 jobs per page requirement, you can use pre_get_posts to filter the main query before it goes to the database and add posts_per_page:

// Lets modify the parameters on the main query
// whenever you create a WP_Query this runs, including the main query! ( aka $q )
add_action( 'pre_get_posts', 'only_2_jobs_per_page' );
function only_2_jobs_per_page( \WP_Query $q ) : void {
    // we only want to change the frontend job post archives
    if ( is_admin() || ! $q->is_main_query() || ! is_post_type_archive( 'jobs' ) ) {
        return;
    }

    // we only want 2 jobs per page:
    $q->set( 'posts_per_page', 2 );
}

With that your pagination will work without any of the weird hacks needed for a custom WP_Query, your code is simpler, and it’ll be twice as fast!

This means that this code:

    $loop = new WP_Query( array( 'post_type' => 'jobs', 'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1, 'posts_per_page' => 2 ) );
    if ( $loop->have_posts() ) :
        while ( $loop->have_posts() ) : $loop->the_post(); ?>

Becomes:

    if ( have_posts() ) :
        while ( have_posts() ) : the_post(); ?>

And this code:

        $total_pages = $loop->max_num_pages;
        if ($total_pages > 1){
            $big = 999999999; // need an unlikely integer
            echo paginate_links( array(
                'base' => str_replace( $big, '%#%', get_pagenum_link( $big ) ),
                'format' => '?paged=%#%',
                'current' => max( 1, get_query_var('paged') ),
                'total' => $loop->max_num_pages
            ) );
        }
        wp_reset_postdata();

Becomes just:

        echo paginate_links();