tax_query not working properly with get_posts

This is strange and should not happen by default. get_posts uses WP_Query. If you look at the source code of get_posts, all the parameters passed to get_posts is passed unchanged to WP_Query except parameters like category which is changed to cat, the include and exclude parameters to include or exclude certain posts which is changed to post__in and post__not_in respectively and numberposts which is changed to posts_per_page.

1839    function get_posts( $args = null ) {
1840            $defaults = array(
1841                    'numberposts' => 5, 'offset' => 0,
1842                    'category' => 0, 'orderby' => 'date',
1843                    'order' => 'DESC', 'include' => array(),
1844                    'exclude' => array(), 'meta_key' => '',
1845                    'meta_value' =>'', 'post_type' => 'post',
1846                    'suppress_filters' => true
1847            );
1848    
1849            $r = wp_parse_args( $args, $defaults );
1850            if ( empty( $r['post_status'] ) )
1851                    $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
1852            if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) )
1853                    $r['posts_per_page'] = $r['numberposts'];
1854            if ( ! empty($r['category']) )
1855                    $r['cat'] = $r['category'];
1856            if ( ! empty($r['include']) ) {
1857                    $incposts = wp_parse_id_list( $r['include'] );
1858                    $r['posts_per_page'] = count($incposts);  // only the number of posts included
1859                    $r['post__in'] = $incposts;
1860            } elseif ( ! empty($r['exclude']) )
1861                    $r['post__not_in'] = wp_parse_id_list( $r['exclude'] );
1862    
1863            $r['ignore_sticky_posts'] = true;
1864            $r['no_found_rows'] = true;
1865    
1866            $get_posts = new WP_Query;
1867            return $get_posts->query($r);
1868    
1869    }

The only noticeable differences are the following two lines

1846     'suppress_filters' => true 
1864     $r['no_found_rows'] = true;

where line 1846 is the most important. By default, get_posts are not altered or interfered with by any filters which act on WP_Query because this is muted by line 1846. WP_Query by default is altered by any external filter that used on it, this goes for the main query and any custom query using WP_Query. That is why it is important to use conditionals like is_main_query() to target the main query specifically.

It is also very very important to know that any poorly written query that is not reset can and will interfere with both WP_Query and get_posts and thus the output from them. query_posts also breaks many functionalities on a page

To debug this, you will need to look for any filters that acts on WP_Query or the main query, also look out for any instance of pre_get_posts. Look for any custom query, and make sure that all instances of WP_Query has been reset with wp_reset_postdata(). If you have used setup_postdata( $post ) with get_posts, then you need to reset that as well. Remove any use of query_posts.

If this does not work, switch to a default theme and test this again. Don’t forget to clear browser and plugin caches first. Lastly, deactivate each plugin one by one and test after each one. This will show you which plugin is causing this if it is plugin related

tech