Custom Login URL (404 mode): fatal error on get_current_screen()

Hi,

Upfront: I’m not a developer. I ran into this error on my site and worked through the diagnosis with Claude (an AI assistant), which also produced the patch below. I’ve tested it and it resolves the issue on my install, but I can’t vouch for it being the right fix architecturally — please treat the analysis as a lead rather than a conclusion.

Symptom

Every time a logged-out visitor hits /wp-admin/ or /wp-login.php, the site throws a PHP fatal error instead of rendering the intended fake 404:

PHP Fatal error: Uncaught Error: Call to undefined function get_current_screen()
in /wp-includes/admin-bar.php:831

Depending on the server’s display_errors setting, this is either shown publicly (leaking server paths and plugin names) or replaced by the “There has been a critical error on this website” page. Either way the module’s goal — hiding the fact that the site runs WordPress — is defeated, and every bot hit fills up the error log.

This happens in the Custom Login URL module (functions/security/custom-login-url.php), with the redirect option set to “404”.

Analysis

render_404_template() loads the front-end rendering engine from the wp_loaded hook, at a point where WP_ADMIN is already defined (the request entered through wp-admin/admin.php). So is_admin() returns true while the functions in wp-admin/includes/ have not been loaded yet.

Two consequences:

  1. Any code — core or plugin — that branches on is_admin() may call an admin function that isn’t available. I hit this first with the acf-openstreetmap-field plugin, then with WordPress core itself.
  2. is_admin_bar_showing() returns true immediately when is_admin() is true, without ever reaching the show_admin_bar filter. The admin bar is therefore built for a logged-out visitor — and it’s wp_admin_bar_edit_menu() calling get_current_screen() that triggers the fatal.

One detail that cost several failed attempts: _wp_admin_bar_init() runs on template_redirect, which is fired by template-loader.php. Nulling $wp_admin_bar before the wp() call has no effect — the object is recreated afterwards.

Reproduction

  • WordPress 7.1, PHP 8.x
  • Classic Monks 2.3.0, Custom Login URL enabled, redirect option set to “404”
  • Oxygen Builder as the site builder
  • Visit /wp-admin/ in a private window

What worked for me

In render_404_template():

// Load the admin functions missing in this hybrid context
if (!function_exists('get_current_screen')) {
    require_once ABSPATH . 'wp-admin/includes/screen.php';
}

// Neutralize the admin bar after the last point where it can be recreated
add_action('template_redirect', static function () {
    $GLOBALS['wp_admin_bar'] = null;
}, PHP_INT_MAX);

The require_once clears the fatal and also guards against other plugins making the same assumption. The add_action prevents the admin bar from being rendered to a logged-out visitor — with no CSS (since wp_enqueue_scripts has already fired), it was showing as raw HTML with the WordPress.org links visible.

With both additions, the fake 404 behaves as intended on my site. Obviously this is a local edit that an update will overwrite, hence the report.

Side note

In this same context, Oxygen injects a footer script containing post_id: , — an empty value producing invalid JavaScript. Harmless as far as I can tell, but possibly another symptom of the same underlying issue.

Happy to provide full stack traces or test any fix you’d like to try. Thanks for your work on the plugin.

Hi @LucileC
Thank you for sharing this in detail, I will be working on it and there will be an update tomorrow probably with this fix.