This issue has been created
There is 1 update.
 
 
XWiki Commons / cid:jira-generated-image-avatar-b51cec7d-c923-4766-91f8-1d3d04e0045a XCOMMONS-3750 Open

LocalExtensionStorage#removeExtension() never deletes the extension version folder

 
View issue   ยท   Add comment
 

Issue created

 
cid:jira-generated-image-avatar-45c40c9b-6691-43e5-a02b-66d9ba1451da Vincent Massol created this issue on 25/Aug/26 14:09
 
Summary: LocalExtensionStorage#removeExtension() never deletes the extension version folder
Issue Type: cid:jira-generated-image-avatar-b51cec7d-c923-4766-91f8-1d3d04e0045a Bug
Affects Versions: 16.10.0
Assignee: Unassigned
Components: Extension
Created: 25/Aug/26 14:09
Priority: cid:jira-generated-image-static-major-36605df3-0209-4119-9b06-e993d5267805 Major
Reporter: Vincent Massol
Description:

Problem

Removing a local extension (uninstalling it, or cleaning it with the Extension Tweak application) leaves its folder behind forever in the local extension repository (<permdir>/extension/repository/ by default), and logs an error:

ERROR o.x.e.r.i.l.DefaultLocalExtensionRepository - Failed to remove extension [...]
java.nio.file.NoSuchFileException: .../repository/<id>/<version>/<id>-<version>.xed

The extension is still correctly removed from memory, so it does disappear from the UI, but the <id>/<version>/ folder is never cleaned up on disk.

Cause

In LocalExtensionStorage#removeExtension(), the block meant to delete the extension version folder deletes the descriptor file a second time instead:

// Get the path to the folder that store the version of the extension being removed
Path extensionVersionFolderPath = extensionDescriptorFilePath.getParent();
try {
    // Delete the extension version folder
    Files.delete(extensionDescriptorFilePath);

    // Try to delete the extension folder
    deleteExtensionFolderIfEmpty(extensionVersionFolderPath.getParent());
} catch (DirectoryNotEmptyException e) {
    ...
}

The descriptor file has already been deleted a few lines above, so this throws NoSuchFileException, which is not caught (only DirectoryNotEmptyException is). As a result:

  • the version folder is never deleted;
  • deleteExtensionFolderIfEmpty() is never reached, so the extension folder is never deleted either โ€“ which means XCOMMONS-3197 never actually worked;
  • the exception propagates to DefaultLocalExtensionRepository#removeExtension(), which logs it as an error.

Two related problems in the same method:

  • the two catch (FileNotFoundException e) blocks around Files.delete() are dead code โ€“ Files.delete() throws NoSuchFileException (a FileSystemException), never FileNotFoundException โ€“ so those warnings can never be logged;
  • the DirectoryNotEmptyException warning logs the descriptor file path instead of the folder path it is about.

Note

Just fixing the target of the delete is not enough. An extension descriptor is also supported directly at the root of the local repository (the repository is loaded by scanning it recursively), in which case the "version folder" is the repository root folder itself, and neither it nor its parent must ever be deleted.

 
 

1 update

 
cid:jira-generated-image-avatar-45c40c9b-6691-43e5-a02b-66d9ba1451da Changes by Vincent Massol on 25/Aug/26 14:09
 
Fix Version: 18.8.0-rc-1