You find a useful snippet, paste it into functions.php or a snippets plugin, click Save, and the site is gone. You get a blank white page, or WordPress’s “There has been a critical error on this website.”
I’ve done this myself more times than I’d like to admit. When it happens right after adding a snippet, one of the most common causes is a PHP fatal error called “Cannot redeclare”. It’s easy to fix once you know what it means. This post covers how to get back in, how to find the real error, and how to write snippets that can’t cause it again. You don’t need any particular plugin for any of it.
Step 1: Get back into your site
Don’t start deleting things. Pick the way back that matches where you added the code.
You edited a file over FTP or your host’s file manager. Open the same file and undo your change: remove the snippet or restore your backup copy. The site comes back as soon as the file is saved.
You used Appearance → Theme File Editor. Since WordPress 4.9, the built-in editor checks the site right after you save a PHP change and undoes the edit if it caused a fatal error (when your server allows that check). If the editor showed an error, your site is probably fine. Reload it to check.
You got the “critical error” message. Check the inbox of the site’s admin email address. Since WordPress 5.2, WordPress emails a recovery mode link when a plugin or theme causes a fatal error. Open the link and log in. The plugin or theme that broke is paused for you only, and you can fix or deactivate it. Note that recovery mode can only pause whole plugins or themes. It can’t pause a single snippet, and it doesn’t cover code in wp-content/mu-plugins/.
You used a snippets plugin. Most have a safe mode that loads the admin without running any snippets. As of October 2026:
- WPCode: add
?wpcode-safe-mode=1to your admin URL, for examplehttps://example.com/wp-admin/?wpcode-safe-mode=1. - Code Snippets: add
define( 'CODE_SNIPPETS_SAFE_MODE', true );towp-config.php. Logged-in admins can also addsnippets-safe-mode=1to the address.
Then switch off the snippet you just added. If nothing else works, rename the snippets plugin’s folder in wp-content/plugins/. WordPress deactivates it, and every snippet in it stops running until you rename it back.
Step 2: Read the real error
The “critical error” page hides the details, which is right for visitors but no help to you. Turn on WordPress’s debug log by adding these lines to wp-config.php, above the line that says “That’s all, stop editing!”:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Reload the broken page, then open wp-content/debug.log. You’ll see something like this (paths shortened; the exact wording varies a little between PHP versions):
PHP Fatal error: Cannot redeclare function show_last_updated()
(previously declared in /wp-content/themes/my-child/functions.php:14)
in /wp-content/plugins/some-plugin/snippets.php on line 3
That one line tells you everything: the function’s name, where it was declared first, and where PHP tried to declare it again.
When you’re done, set WP_DEBUG back to false, and delete debug.log. It sits at a well-known address, and on many servers anyone can open it.
Step 3: Understand why it happens
In PHP, every named function exists once for the whole request. Your theme, every plugin and every snippet share the same space. When two pieces of code both say function show_last_updated(), PHP stops at the second one with a fatal error.
After adding a snippet, the usual causes are:
- The same snippet in two places. It’s in your child theme’s
functions.phpand in a snippets plugin, often left over from an earlier attempt. - A generic name. Tutorials love names like
custom_excerpt_length()oradd_custom_fonts(), and so do some themes and plugins. - A function declared inside another function that runs twice (for example, inside a hook callback). The first run declares it, the second run crashes.
- A file loaded twice with
includeorrequireinstead ofinclude_onceorrequire_once.
Syntax checkers like php -l won’t catch this, because each file is valid PHP on its own. The problem only appears when both are loaded together.
Step 4: Fix it
Take this typical snippet, which adds a “Last updated” line to blog posts:
<?php
function show_last_updated( $content ) {
// …
}
add_filter( 'the_content', 'show_last_updated' );
You have four options, from best to least good.
1. Delete the duplicate. If the debug log shows the same code in two places, keep one. Done.
2. Use a closure, so there’s no name to clash. This is how I write every snippet now:
<?php
add_filter(
'the_content',
static function ( $content ) {
if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
$updated = sprintf(
/* translators: %s: the date the post was last changed. */
__( 'Last updated: %s', 'my-site' ),
get_the_modified_date()
);
return $content . '<p class="last-updated">' . esc_html( $updated ) . '</p>';
}
);
A closure (an anonymous function) has no global name, so it can’t collide with anything, even if you paste the snippet twice. The one trade-off is that other code can’t remove it later with remove_filter(). For a site’s own snippets, that’s rarely a problem.
3. Give it a unique prefix. If you need a named function (for example, because another snippet calls it), prefix it with something only you would use, like your initials or the site’s name: acme_show_last_updated(). A clash then becomes very unlikely.
4. Wrap it in function_exists(), carefully.
<?php
if ( ! function_exists( 'acme_format_price' ) ) {
function acme_format_price( float $amount ): string {
return number_format_i18n( $amount, 2 );
}
}
This stops the crash, but read it closely: if a function with that name already exists, yours is silently skipped and the other one runs instead, even if it does something completely different. Use it for helpers you deliberately share between your own snippets, not as a quick way to silence the error.
If the function is declared inside another function or hook, move the declaration out to the top level of the file (or turn it into a closure). If a file is included twice, switch to require_once.
The same rules apply to classes. “Cannot declare class X, because the name is already in use” has the same causes and the same fixes: delete the duplicate, use a unique prefix or namespace, or check with class_exists().
Step 5: Make the next snippet safer
- Keep all your custom code in one place: a child theme, a must-use plugin or a snippets plugin, not a mix of all three.
- Try new code on a staging copy first. Many hosts create one in a click.
- Prefer closures for hook callbacks, and prefix anything that must have a name.
- Keep a way back you’ve already tested: FTP access, your snippets plugin’s safe mode, or a backup you know how to restore.
Where Snipfire helps
I built Snipfire, a free snippets plugin, partly because of this exact error. When you save a PHP snippet, it checks the syntax and warns you if a function or class you’re declaring already exists, before anything runs. When you switch a snippet on, it test-loads your site first and switches the snippet straight back off if the site would crash. If a snippet causes a fatal error later, it’s switched off automatically, and you see the exact line. If all else fails, safe mode (a secret link, or define( 'SNIPFIRE_SAFE_MODE', true ); in wp-config.php) gets you back in. The Safety page shows how each step works.
You can try it in your browser without installing anything, or download it free. But everything above works with whatever you use today.
The short version
- Get back in first: undo the file edit, use the recovery mode email, or use your snippets plugin’s safe mode.
- Turn on
WP_DEBUG_LOGand readdebug.log. It names both places the function was declared. - Delete the duplicate, or rewrite the snippet as a closure so there’s no name to clash.
- Use
function_exists()only when you understand that it skips your version.
Stuck on an error that isn’t covered here? Send it to me and I’ll take a look.
Antony, maker of Snipfire