{"record":{"id":"d8b9f40b05f1e2c3","repo":"octobercms/october","slug":"editor-lang-filesystem-error-deleting-file","errorCode":null,"errorMessage":"editor::lang.filesystem.error_deleting_file","messagePattern":"editor::lang\\.filesystem\\.error_deleting_file","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"error","filePath":"modules/editor/traits/FileSystemFunctions.php","lineNumber":132,"sourceCode":"     * editorDeleteFileOrDirectory\n     */\n    protected function editorDeleteFileOrDirectory($basePath, $fileList)\n    {\n        // Delete leaves first\n        usort($fileList, function($a, $b) {\n            return strlen($b) - strlen($a);\n        });\n\n        foreach ($fileList as $path) {\n            if (!$this->validateFileSystemPath($path)) {\n                throw new ApplicationException(Lang::get('editor::lang.filesystem.invalid_path'));\n            }\n\n            $fullPath = $basePath.'/'.$path;\n            if (File::exists($fullPath)) {\n                if (!File::isDirectory($fullPath)) {\n                    if (!@File::delete($fullPath)) {\n                        throw new ApplicationException(Lang::get(\n                            'editor::lang.filesystem.error_deleting_file',\n                            ['name' => $path]\n                        ));\n                    }\n                }\n                else {\n                    $empty = File::isDirectoryEmpty($fullPath);\n                    if (!$empty) {\n                        throw new ApplicationException(Lang::get(\n                            'editor::lang.filesystem.error_deleting_dir_not_empty',\n                            ['name' => $path]\n                        ));\n                    }\n\n                    if (!@rmdir($fullPath)) {\n                        throw new ApplicationException(Lang::get(\n                            'editor::lang.filesystem.error_deleting_dir',\n                            ['name' => $path]","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/editor/traits/FileSystemFunctions.php#L114-L150","documentation":"Thrown by editorDeleteFileOrDirectory when File::delete() (error-suppressed) returns false for a regular file (lines 129-136). Message interpolates the path: 'Error deleting file :name.'. unlink() failing at this point almost always means the PHP user cannot write the containing directory (deleting a file modifies the directory entry), or the file is locked/immutable.","triggerScenarios":"command_onAssetDelete on files under themes/<theme>/assets when the directory is owned by root or another user; a file held open by another process on Windows; the 'i' immutable flag set by a deploy hardening step; read-only mount.","commonSituations":"CI/CD or dockerized setups that build themes as root while php-fpm runs as www-data; hardened servers with immutable web assets; NFS/SMB mounts without write perms for the webserver user.","solutions":["Grant the PHP user write on the directory that holds the file: chown/chmod the parent dir (e.g. chmod u+w themes/demo/assets/css)","Check locks: lsof +D themes/<theme>/assets on Linux, or close the editor/IDE holding it on Windows","Clear the immutable flag if set: lsattr <file> then chattr -i <file>","Verify the volume is mounted read-write for the webserver user"],"exampleFix":"# before: dir not writable by webserver -> unlink fails\n$ sudo lsattr themes/demo/assets/css/app.css  # shows 'i'\n# after\n$ sudo chattr -i themes/demo/assets/css/app.css\n$ sudo chown www-data: themes/demo/assets/css","handlingStrategy":"try-catch","validationCode":"$full = $assetsBase.'/'.$path;\nif (file_exists($full) && !is_writable(dirname($full))) {\n    // deletion modifies the DIRECTORY entry — fix dir perms first\n}","typeGuard":null,"tryCatchPattern":"try {\n    if (!@File::delete($fullPath)) {\n        throw new ApplicationException(Lang::get('editor::lang.filesystem.error_deleting_file', ['name' => $path]));\n    }\n} catch (ApplicationException $e) {\n    Log::warning('asset delete failed', ['path' => $path, 'user' => get_current_user()]);\n    throw $e; // surface to the AJAX client, or queue for manual cleanup\n}","preventionTips":["Ensure every assets subdirectory is writable by the PHP user (delete needs dir write, not file write)","Run deploys as the same user as php-fpm/www-data, or chown afterwards","Watch for the immutable flag ('i') on hardened servers before blaming the code"],"tags":["filesystem","permissions","io","editor","winter-cms"],"backgroundTag":"filesystem-permission-denied","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}