When I try to upload a file with a file field, Drupal creates a sites/default/files/2023-10 directory, but can't upload the file. I see this validation error in the UI: "File upload error. Could not move uploaded file." And I see a similar notice in the Drupal log: "Upload error. Could not move uploaded file test.txt to destination public://2023-10/test.txt."

In admin/config/media/file-system, my public file system path is set to sites/default/files. There are no errors on the status page indicating a permissions problem.

If I delete the files directory altogether, and then save the form at admin/config/media/file-system, Drupal creates sites/default/files and sites/default/files/php.

What are the possible reasons Drupal can create directories inside of sites/default/files, but can't upload files?

Drupal version: 9.5.11
Web server: IIS 10
PHP 8.1.21
  • Is there any specific error you are getting while upload files in the logs either in console, try to upload file of smaller size like 2MB may be issue of file size larger Commented Oct 26, 2023 at 14:14
  • A possible reason is that whatever user context IIS or Drupal is running in can create directories but not files. I don't think we can give a fact-based acceptable answer without information from you as to what the filesystem permissions actually are.
    – mona lisa
    Commented Oct 26, 2023 at 14:16
  • @mini The file size isn't the issue. I updated the ticket with the notice I'm seeing in the log, though it is similar to the validation error displayed in the UI.
    – arnoldbird
    Commented Oct 26, 2023 at 15:13
  • 2
    Please be aware of drupal.org/project/drupal/issues/3358248
    – mona lisa
    Commented Oct 26, 2023 at 15:49
  • 1
    Windows filesystem has no concept of 777 AFAIK.
    – mona lisa
    Commented Oct 26, 2023 at 17:03

2 Answers 2


I needed to set the upload_tmp_dir in PHP.ini. This is what worked for me:

upload_tmp_dir = c:\PHP\tmp

I also gave full permissions to the IUSR user for that directory.


It seems it is bug/feature at least in PHP 8.1.27 in move_uploaded_file() resp. is_uploaded_file() which is used in in Drupal 9.5 code:

public function moveUploadedFile($filename, $uri) {
    file_put_contents('c:\temp\drupal\debug.txt', "filename:" . var_export($filename, true) . "\nis_uploaded:" . var_export(is_uploaded_file($filename), true) . "\nUri:" . var_export($uri, true) ."\n", FILE_APPEND);
        $result = @move_uploaded_file($filename, $uri);
    file_put_contents('c:\temp\drupal\debug.txt', "result:" . var_export($result, true)."\n", FILE_APPEND);
        // PHP's move_uploaded_file() does not properly support streams if
        // open_basedir is enabled so if the move failed, try finding a real path
        // and retry the move operation.
        if (!$result) {
          if ($realpath = $this->realpath($uri)) {
    file_put_contents('c:\temp\drupal\debug.txt', "Realpath: ".var_export($realpath, true)."\n", FILE_APPEND);
            $result = move_uploaded_file($filename, $realpath);
    file_put_contents('c:\temp\drupal\debug.txt', "Result: ".var_export($result, true)."\nError:".var_export(error_get_last(), true)."\n", FILE_APPEND);
          else {
    file_put_contents('c:\temp\drupal\debug.txt', "uri:" . var_export($uri, true)."\n", FILE_APPEND);
            $result = move_uploaded_file($filename, $uri);
        return $result;


upload_tmp_dir = C:\Temp

but C:\Temp is link to C:\Users\TMa\AppData\Local\Temp

Under this condition is_uploaded_file() returns False and move_uploaded_file() fails because of security reasons.

Fix was simple, just set real path in PHP.ini

upload_tmp_dir = C:/Users/TMa/AppData/Local/Temp

Debug log c:\temp\drupal\debug.txt:

Before fix:

Wrapper: Drupal\Core\StreamWrapper\PublicStream::__set_state(array(
   'context' => NULL,
   'handle' => NULL,
   'uri' => 'public://2024-05/cv_debug.txt',
Realpath: 'E:/Www/drupal-9.5/sites/xxx/files/public/2024-05/cv_debug.txt'
Result: false



Note: I originally thought that issue is mixing "\" and "/" in path but this is not problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.