Skip to content
ThemesIonic — home
Troubleshooting

Fatal Error: Cannot Redeclare — Two Files Define the Same Function

The message names both files that define the function. Which two they are — a child and parent theme, two copies of one plugin, a snippet pasted twice, or PHP itself — decides the fix, and a function_exists() guard only helps on the file that loads second.

5 min read intermediate

Two pieces of code define a function with the same name, and PHP refuses the second one; the message prints both locations. On PHP 8.4 and newer it reads PHP Fatal error: Cannot redeclare function mytheme_setup() (previously declared in /var/www/html/wp-content/themes/mytheme-child/functions.php:12) in /var/www/html/wp-content/themes/mytheme/functions.php on line 40. PHP 8.0 to 8.3 print the same thing without the word "function": Cannot redeclare mytheme_setup() (previously declared in …).

The "previously declared in" path is the definition that won. The "in … on line" path is the one that crashed. Fix either one, but first work out why both exist.

Read the two paths

First path Second path Diagnosis
themes/child/functions.php themes/parent/functions.php Child theme copied the parent's functions
plugins/foo/… plugins/foo-1/… (or foo-old, foo-copy) Two copies of the same plugin, both active
plugins/code-snippets-… or functions.php the other one The same snippet pasted in two places
Any file The same file The file is included twice with include/require
(none) Any file The function is built into PHP

Get the full line from debug.log with debug logging on; the "critical error" screen shows none of it.

Child theme copied the parent's functions.php

WordPress includes the child theme's functions.php first and the parent's afterwards. Copying the parent's whole file into the child — a common misunderstanding of how child themes work — declares every function twice, and the parent crashes on the first one.

The child's functions.php does not replace the parent's; both run. Remove the copied functions from the child and keep only your additions. To change a parent function you have two clean options:

  • If the parent wraps it in if ( ! function_exists( 'mytheme_setup' ) ), it is pluggable: declare your version in the child and the parent skips its own.
  • If it is not wrapped, leave it alone and change its behaviour through the hooks and filters it uses, or remove_action() it and add your own under a different name.

Two copies of one plugin

Uploading a plugin zip when the folder already exists, or restoring a backup next to a live copy, leaves wp-content/plugins/foo/ and foo-1/. Activate both and every function is declared twice. The two paths in the message differ only in the folder name, which is easy to miss when you skim.

Deactivate and delete the copy you do not want — compare versions in the plugin header first — and keep one folder.

A snippet in two places

A function pasted into functions.php and also into a snippets plugin, or added to the theme after a plugin started shipping the same helper, collides the same way. Remove one copy. When it is your own code, give every function a project prefix (acme_) or put it in a namespace; unprefixed names like custom_excerpt_length() are exactly the ones two authors pick independently. functions.php explained covers where custom code belongs.

The same file loaded twice

If both paths are the same file, something includes it twice. require 'file.php' runs the file again every time; require_once checks first. Look at who includes the file, usually a theme template or a plugin bootstrap, and switch to require_once or include it from a single place. For classes the message changes: PHP 8.4 and newer say Cannot redeclare class Foo (previously declared in …), older versions Cannot declare class Foo, because the name is already in use.

PHP itself already has the function

No "previously declared in" part means the first definition is PHP's own. Old plugins and themes shipped polyfills such as get_debug_type(), json_validate() or mb_trim() for PHP versions that lacked them. Without a function_exists() check, the polyfill fatals as soon as the host upgrades PHP to a version where the function is built in. WordPress's own polyfills in wp-includes/compat.php are all guarded, so the culprit is always third-party. There is a twist: core polyfills many PHP functions itself — str_contains(), str_starts_with() and str_ends_with() since WordPress 5.9, and newer ones such as array_find(), array_first() and array_last(). Core loads before any plugin, so an unguarded plugin polyfill of one of those fails even on an older PHP version, and this time the "previously declared in" path is wp-includes/compat.php.

Update the plugin or theme; if it is abandoned, wrap the declaration (here, an old str_contains() polyfill):

if ( ! function_exists( 'str_contains' ) ) {
	function str_contains( $haystack, $needle ) {
		return '' === $needle || false !== strpos( $haystack, $needle );
	}
}

The same upgrade tends to surface other errors at once; see site not working after a PHP update.

The guard trap

function_exists() only prevents the later declaration. Load order is fixed: must-use plugins, then regular plugins, then the child theme, then the parent theme. A guard in a plugin does nothing when the theme's unguarded copy loads after it — the theme still crashes. Put the guard on the file that loads second, or better, rename one of the functions so there is nothing to guard.

Common mistakes

  • Deleting the function from whichever file you opened first, without checking which copy the site's callers expect.
  • Copying the parent functions.php into the child theme "to edit it".
  • Renaming the plugin folder to fix it, which only deactivates that copy until someone reactivates both.
  • Adding function_exists() to the copy that loads first.

Verify

  • grep -rn "function mytheme_setup" wp-content/ returns one definition, or one plus guarded ones.
  • The site loads and debug.log has no new "Cannot redeclare" line.
  • Plugins › Installed Plugins lists one copy of each plugin.

Frequently asked

Because the first definition is not in a PHP file: the function is built into PHP. That happens when old code ships its own version of a function such as json_validate() without checking function_exists(), and the server moves to a PHP version that has it natively. Update or patch the code that declares it.
Yes, for classes. PHP 8.3 and older word it that way; PHP 8.4 and newer say Cannot redeclare class Foo and add the previously declared location. The causes are the same: two copies of a plugin or library, or a file included twice with require instead of require_once.
Only if your copy is the one that loads second. The guard skips a declaration when the function already exists; it cannot undo one that was made earlier. Plugins load before themes, and a child theme's functions.php loads before the parent's, so put the guard on the later file or rename your function with a unique prefix.

Related guides