Last active
June 21, 2026 18:14
-
-
Save robwent/71b4a86b3bcc3fd7d740f486c67d915b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Plugin Name: Elementor Pro – Restore 404s on Non-Existent Term Archives | |
| * Description: Fixes a fatal error ("Object of class WP_Error could not be converted to string") and incorrect 200 responses on non-existent tag/category/taxonomy URLs when an Elementor Pro Archive template with a paginated posts widget is active. | |
| * Version: 1.0.0 | |
| * Author: https://robertwent.com | |
| * License: GPL-2.0-or-later | |
| * | |
| * --------------------------------------------------------------------------- | |
| * THE PROBLEM | |
| * --------------------------------------------------------------------------- | |
| * On a site using an Elementor Pro Theme Builder "Archive" template that | |
| * contains a Posts / Loop Grid / Archive Posts widget with pagination enabled | |
| * (especially "Load More" / infinite scroll, or with no page limit), visiting a | |
| * term archive URL whose term does NOT exist — e.g. /tag/does-not-exist/ — | |
| * produces one of two broken results: | |
| * | |
| * 1. A fatal error: | |
| * PHP Fatal error: Uncaught Error: Object of class WP_Error could not be | |
| * converted to string in .../elementor/core/dynamic-tags/manager.php | |
| * ...if the template uses the "Archive URL" dynamic tag anywhere. Elementor | |
| * Pro's Utils::get_the_archive_url() calls get_term_link( get_queried_object() ) | |
| * without an is_wp_error() guard; for a missing term that returns a WP_Error, | |
| * which Elementor then tries to cast to a string while generating CSS. | |
| * | |
| * 2. Failing that, an HTTP 200 response for a page that should be a 404. | |
| * | |
| * THE CAUSE | |
| * --------------------------------------------------------------------------- | |
| * Elementor Pro adds a `pre_handle_404` filter | |
| * (Locations_Manager::should_allow_pagination_on_archive_templates) so that | |
| * paginated archive templates don't get 404'd on page 2+. With Load More / | |
| * infinite-scroll pagination (or no page limit) it returns "handled" even on | |
| * page 1 — for ANY archive matching the template, including URLs whose term | |
| * doesn't exist. That short-circuits WordPress's own handle_404(), so set_404() | |
| * never runs: the status stays 200 and is_tag()/is_tax() stay true, which is | |
| * what exposes the WP_Error in the Archive URL dynamic tag. | |
| * | |
| * THE FIX | |
| * --------------------------------------------------------------------------- | |
| * Hook `pre_handle_404` at a later priority than Elementor (which uses 10/11). | |
| * When the request is a term archive (tag/category/custom taxonomy) whose | |
| * queried object is NOT a real WP_Term, force a genuine 404 right there — at | |
| * the canonical 404 decision point, before template selection and wp_head, so | |
| * is_tag() is already false when Elementor renders. Real terms (even empty | |
| * ones) and legitimate paginated archives are left completely untouched. | |
| * | |
| * Drop this file into wp-content/mu-plugins/ (it loads automatically). It is | |
| * harmless if Elementor Pro is not installed. | |
| * --------------------------------------------------------------------------- | |
| * | |
| * @package ElementorMissingTerm404Fix | |
| */ | |
| if ( ! defined( 'ABSPATH' ) ) { | |
| exit; | |
| } | |
| add_filter( 'pre_handle_404', 'em404_force_404_on_missing_term', 99, 2 ); | |
| /** | |
| * Force a proper 404 when a term archive's queried object can't be resolved. | |
| * | |
| * @param bool $handled Whether a plugin has already declared the request handled. | |
| * @param \WP_Query $wp_query The query that triggered handle_404(). | |
| * @return bool True if we've handled it (as a genuine 404), otherwise the value unchanged. | |
| */ | |
| function em404_force_404_on_missing_term( $handled, $wp_query ) { | |
| // Only term archives: tag, category, or any custom taxonomy. | |
| if ( ! ( $wp_query->is_tag || $wp_query->is_category || $wp_query->is_tax ) ) { | |
| return $handled; | |
| } | |
| // A valid term archive resolves to a WP_Term. Anything else (null / WP_Error) | |
| // means the term doesn't exist, so this should be a 404. | |
| if ( $wp_query->get_queried_object() instanceof WP_Term ) { | |
| return $handled; | |
| } | |
| $wp_query->set_404(); | |
| status_header( 404 ); | |
| nocache_headers(); | |
| return true; // We've handled it — as a real 404. | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Blog post: Fixing Elementor Pro Soft 404s (and a Fatal) on Non-Existent Tag and Category URLs