octobercms/october · warning · ApplicationException

editor::lang.filesystem.error_deleting_dir_not_empty

Error message

editor::lang.filesystem.error_deleting_dir_not_empty

What it means

Thrown by editorDeleteFileOrDirectory when a selected path is an existing directory that is NOT empty (File::isDirectoryEmpty returns false, lines 138-145). By design the delete command only removes empty directories — there is no recursive delete. The usort by strlen descending deletes leaves first, so a multi-select containing a parent AND all its children works, but any unselected child aborts with 'Error deleting directory :name. The directory is not empty.'.

Source

Thrown at modules/editor/traits/FileSystemFunctions.php:141

        foreach ($fileList as $path) {
            if (!$this->validateFileSystemPath($path)) {
                throw new ApplicationException(Lang::get('editor::lang.filesystem.invalid_path'));
            }

            $fullPath = $basePath.'/'.$path;
            if (File::exists($fullPath)) {
                if (!File::isDirectory($fullPath)) {
                    if (!@File::delete($fullPath)) {
                        throw new ApplicationException(Lang::get(
                            'editor::lang.filesystem.error_deleting_file',
                            ['name' => $path]
                        ));
                    }
                }
                else {
                    $empty = File::isDirectoryEmpty($fullPath);
                    if (!$empty) {
                        throw new ApplicationException(Lang::get(
                            'editor::lang.filesystem.error_deleting_dir_not_empty',
                            ['name' => $path]
                        ));
                    }

                    if (!@rmdir($fullPath)) {
                        throw new ApplicationException(Lang::get(
                            'editor::lang.filesystem.error_deleting_dir',
                            ['name' => $path]
                        ));
                    }
                }
            }
        }
    }

    /**
     * editorMoveFilesOrDirectories

View on GitHub (pinned to b608633a7e)

Solutions

  1. Expand the directory in the sidebar and select ALL of its contents together with the directory itself (the strlen sort then removes children first)
  2. Empty the directory first (delete its files), then delete the directory in a second step
  3. Refresh the tree and retry if another session may have written files meanwhile
  4. Remove invisible leftovers on disk (rm -rf themes/<theme>/assets/<dir>) when the sidebar cannot see them

Example fix

// before
files = ['js/lib']                    // contains jquery.js -> blocked
// after
files = ['js/lib/jquery.js', 'js/lib'] // child first, parent then empties
Defensive patterns

Strategy: validation

Validate before calling

foreach ($files as $path) {
    $full = $assetsBase.'/'.$path;
    if (is_dir($full) && !(new \FilesystemIterator($full))->valid()) {
        // directory has entries: include them in $files or empty it first
    }
}

Prevention

When it happens

Trigger: command_onAssetDelete where 'files' includes a directory but not every entry inside it (the sidebar selection missed nested or hidden files), or a file was created inside the directory between selection and the request. Because processing is longest-path-first, a missed child typically fails the parent AFTER some children were already deleted.

Common situations: Selecting a folder node without expanding/selecting its contents in the tree; dotfiles (.gitkeep, .DS_Store) not shown in the sidebar but counted by isDirectoryEmpty; concurrent uploads/builds writing into the directory during the delete.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/52488e0339194d26. Report an issue: GitHub.