WordPress Update Failed: Could Not Create Directory, Explained
On this page, 10 sections
“Update failed: Could not create directory” means PHP, running as the web server user, was refused when it tried to make a folder during an update. The first thing to check is file ownership: the account PHP runs as does not own, or cannot write to, a directory inside wp-content.
The usual advice is to set permissions to 755 and 644 and move on. That is partly right and leaves out the check WordPress itself runs. This post follows the code from the failing mkdir back to the ownership test, so you know what to look for.
I read WordPress 7.1.3 source on 11 October 2026. I did not reproduce the error and I ran nothing on a live site. Where I reason beyond what the source says, I mark it.
Where the message comes from
The text is defined in wp-admin/includes/class-wp-upgrader.php, where mkdir_failed is the string “Could not create directory.” The error is raised in several places in wp-admin/includes/file.php: the ZipArchive unzip path (line 1782), the PclZip path (line 1968), copy_dir() (line 2061) and move_dir() (line 2144).
The one that matters most is copy_dir(). It copies the unzipped package from wp-content/upgrade/ to its destination, creating each subfolder as it goes. When a mkdir fails, it returns an error whose data is the path it tried to create, which is the destination plus the folder name. A different message, “Could not copy file.”, covers the file-level failure at line 2053.
Two things follow. The failing folder can be deep inside a plugin, not only at the top of wp-content. And the failure depends on the user PHP runs as, not on the user you log in as over SSH.
How WordPress decides it can write directly
Before any file is touched, get_filesystem_method() in file.php picks how to write. If the FS_METHOD constant is set, that wins.
Otherwise WordPress creates a temporary file in the directory it is about to update, by default wp-content. It compares the owner of file.php itself with the owner of that temporary file. Only if the two owners match does it choose “direct”.
If they do not match, WordPress falls back to FTP or SSH and shows the credentials prompt. That prompt is the other face of the same problem: it means the owner of your files is not the user PHP runs as.
Here is my reading, not a test. The ownership test touches one directory, wp-content or whichever the upgrader passes. It does not check the folders inside it.
If WordPress passes the test and then fails with “Could not create directory”, a deeper folder is probably owned by someone else. A common way that happens is running WP-CLI as root: it creates files and folders owned by root inside a tree that PHP normally owns.
Find the folder PHP cannot write
Test as the user PHP runs as, not as yourself. First find that user:
ps -eo user,comm | grep -i php-fpm | sort -u
Then list every directory under wp-content that user cannot write to. Replace the user and path with your own:
sudo -u www-data find /var/www/example/wp-content -type d ! -writable
Any line printed is a candidate. Check the owner of each:
ls -ld /var/www/example/wp-content/plugins/some-plugin
If the owner is root, or a different account from the rest of the tree, you have found the cause. The fix is to give the tree back to the PHP user. Take a backup first; what a WordPress backup must contain lists the pieces.
sudo chown -R www-data:www-data /var/www/example/wp-content
Use the user and group your server really uses. On a host where each site has its own user, that is the site user and not www-data. The permission values themselves are covered in WordPress file permissions that matter.
Do not force the filesystem method
Some guides tell you to add define( 'FS_METHOD', 'direct' ); to wp-config.php. Because the constant is honoured before the ownership check, it silences the FTP prompt. It does not give PHP permission to write. If the folder is owned by root, the mkdir fails exactly as before, and now you have hidden the one clue that told you why.
Use it only when the ownership check gives a wrong answer on a setup where writing works, such as some container or shared-storage layouts. Verify with the find test first.
Disk space, the cron-only check
Running out of room produces a related message: “Could not copy files. You may have run out of disk space.” In _unzip_file_ziparchive(), WordPress multiplies the uncompressed size of the package by 2.1 to estimate the space it needs. The comment in the source describes it as a 10 percent buffer. The error code is disk_full_unzip_file.
Look at the guard around it. The check runs only if ( wp_doing_cron() ), so it applies to automatic background updates and not to an update you start from the dashboard. A manual update that hits a full disk fails later, at the write, with a less helpful message.
For a hypothetical package that expands to 50 MB, 50 times 2.1 is 105 MB of free space needed. I computed that in Python; the 50 MB is an example, not a measured plugin. Check your own free space:
df -h /var/www/example/wp-content
Core also handles a full disk in its own upgrader, in class-core-upgrader.php, and treats a disk_full error as critical, attempting a rollback.
Leftovers from a failed update
A failed update can leave two things behind. The first is a leftover working folder in wp-content/upgrade/. The upgrader clears that folder at the start of the next attempt, so it is not usually the problem. The second is a .maintenance file in the site root, which keeps the site in maintenance mode. That has its own post: WordPress stuck in maintenance mode.
If automatic updates fail on a schedule and not only when you click, read How to fix WordPress automatic updates not working. If the site shows a blank page after the failure, WordPress white screen: read the debug log covers recovery.
How this was checked
I read file.php and class-wp-upgrader.php from the WordPress 7.1.3 tag on GitHub on 11 October 2026, plus class-core-upgrader.php for the disk-full branch. Line numbers are from that tag. I computed 50 times 2.1 in Python. I ran bash -n on the shell commands.
I did not reproduce the error, did not test a root-owned folder, and did not run find or chown on any server. The idea that a deeper folder owned by root is the usual cause is my reading of where the ownership test stops, and I have not measured how often it is true.
Questions people ask
Will setting permissions to 777 fix it?
It may hide the error, and it makes every file world-writable. The problem is ownership, so fix that instead.
Why does the FTP credentials prompt appear?
Because the owner of the WordPress files differs from the owner of a file PHP just created. WordPress takes that as a sign it cannot write directly.
Does it matter which user I use over SSH?
Yes. Files created by root while you work over SSH can belong to root afterwards, and PHP then cannot write them.
Do this today
Run the find command above as your PHP user. If it prints nothing, ownership is not your problem and the next place to look is free disk space.
Resources
- WordPress 7.1.3, wp-admin/includes/file.php, copy_dir() and get_filesystem_method(), read 11 October 2026
- WordPress 7.1.3, wp-admin/includes/class-wp-upgrader.php, read 11 October 2026
- WordPress 7.1.3, wp-admin/includes/class-core-upgrader.php, read 11 October 2026