Display posts from only one post form in custom query and exclude in main query

I don’t suspect that your tax_query is failing you, but actually your tag parameter is. single_tag_title() returns the name of the tag, not the slug.

All tag and category queries are converted to a tax_query in the WP_Query class before being passed to the WP_Tax_Query class to build the relative string for the SQL query. Term names and slugs gets sanitized before validation and before getting the term ID of the passed term/s. (Just for interest sake, read why you should not use term names and the name parameter inside a tax_query, check my answer here)

Now, slugs should always be lowercase and multiple words separated by hyphens. You are passing a name as a slug. Because the term name matches nothing in the term slug column (because you have told the tax_query to look in the slug column), WP_Tax_Query accepts that the term is invalid and does not exist and therefor simply bails out and return an empty string. This is where the big flaw comes in (which in my opinion is a bug that should be fixed). WP_Tax_Query sends an empty SQL join string back to WP_Query, WP_Query wrongly reads this as if there were never a tax_query and continues to build and then executes the SQL without the join clause. This unexpectedly then returns all posts regardless, where in my opinion it should just return no posts at all. This is what you are seeing

So lets look at a solution and also clean up your code and make it more reliable

First, your pre_get_post action

  • Add a check to target the front end only, otherwise back end queries will be affected as well

  • Set is_tag() to the current instance

  • Get the current tax_query on the tag page and modify it to add the the post format section and pass that as a new tax_query

You can adjust your code to something like this: (I’m using closures, but you can revert to the old style as in your question 😉)

add_action('pre_get_posts', function ($q) 
{
    if (    !is_admin() // Only targets front end queries
         && $q->is_main_query() // Only targets the main query
         && $q->is_tag() // Only targets tag archive pages  
    ) {
        // Gets the current tax_query
        $tax_query_obj = $q->tax_query->queries;
        // Add the post format query part to the current tax_query
        $tax_query_obj[] = [
            'taxonomy' => 'post_format',
            'field' => 'slug',
            'terms' => ['post-format-aside'],
            'operator' => 'NOT IN'
        ];
        //Build a proper tax query
        $tax_query = [
            'relation' => 'AND',
            $tax_query_obj
        ];
        // Set the modified tax_query
        $q->set( 'tax_query', $tax_query );

        // Set any additional parameters here except tag parameters
    }
}, PHP_INT_MAX );

As for your custom query, we can get the current tag ID being viewed by simply using get_queried_object_id() and then using that in a tax_query with our post format . You can try the following in your arguments: (Requires PHP 5.4+ due to new short array syntax ([]))

$args = [
    'tax_query' => [
        [
            'taxonomy' => 'post_tag', // Default tag taxonomy
            'terms' => get_queried_object_id(), // Gets current tag archive tag ID
        ],
        [
            'taxonomy' => 'post_format',
            'field' => 'slug',
            'terms' => 'post-format-aside'
        ],
    ],
    // Rest of your arguments excluding tag parameter
];