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]
));
}
}
}
}
}
/**
* editorMoveFilesOrDirectoriesView on GitHub (pinned to b608633a7e)
Solutions
- Expand the directory in the sidebar and select ALL of its contents together with the directory itself (the strlen sort then removes children first)
- Empty the directory first (delete its files), then delete the directory in a second step
- Refresh the tree and retry if another session may have written files meanwhile
- 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
- Always include ALL children (recursively) in the delete selection alongside the folder
- Remember the API deletes empty directories only — there is no recursive delete
- Refresh before deleting if build tools may have written files into the folder
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
- editor::lang.filesystem.type_not_allowed
- editor::lang.filesystem.selected_files_not_found
- editor::lang.filesystem.select_destination_dir
- editor::lang.filesystem.destination_exists
- editor::lang.filesystem.directory_name_cant_be_empty
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/52488e0339194d26.
Report an issue: GitHub.