Leantime/leantime · error · Exception
notification.plugin_zip_cant_extract
Error message
notification.plugin_zip_cant_extract
What it means
After ZipArchive::open() succeeded, extractTo($pluginDir) returned false: 'Could not extract plugin'. The archive opened fine but one or more entries could not be written or decompressed - per-entry CRC failures, a compression method libzip cannot extract, insufficient permissions/space in the freshly created app/Plugins/{Folder}, or entries whose paths cannot be created.
Source
Thrown at app/Domain/Plugins/Services/Plugins.php:749
$zip = new \ZipArchive;
match ($zip->open($temporaryFile)) {
\ZipArchive::ER_EXISTS => throw new \Exception(__('notification.plugin_zip_exists')),
\ZipArchive::ER_INCONS => throw new \Exception(__('notification.plugin_zip_inconsistent')),
\ZipArchive::ER_INVAL => throw new \Exception(__('notification.plugin_zip_invalid_arg')),
\ZipArchive::ER_MEMORY => throw new \Exception(__('notification.plugin_zip_malloc')),
\ZipArchive::ER_NOENT => throw new \Exception(__('notification.plugin_zip_no_file')),
\ZipArchive::ER_NOZIP => throw new \Exception(__('notification.plugin_zip_not_zip')),
\ZipArchive::ER_OPEN => throw new \Exception(__('notification.plugin_zip_cant_open')),
\ZipArchive::ER_READ => throw new \Exception(__('notification.plugin_zip_read_err')),
\ZipArchive::ER_SEEK => throw new \Exception(__('notification.plugin_zip_seek_err')),
default => throw new \Exception(__('notification.plugin_zip_unknown_err')),
true => null,
};
if (! $zip->extractTo($pluginDir)) {
throw new \Exception(__('notification.plugin_zip_cant_extract'));
}
$zip->close();
unlink($temporaryFile);
// read the composer.json content from the plugin phar file
$pluginModel = $this->createPluginFromComposer($foldername, $plugin->license);
if (! $this->pluginRepository->addPlugin($pluginModel)) {
throw new \Exception(__('notification_cant_add_to_db'));
}
}
/**
* Validates the license of a given plugin.
*
* @param InstalledPlugin $plugin The plugin object for which the license validity is being checked.View on GitHub (pinned to 9a9f49f100)
Solutions
- Verify free space covers the uncompressed size: df -h app/Plugins (zips typically expand 2-5x).
- Confirm the PHP user can write into app/Plugins/{Folder}: touch a test file as that user.
- Copy the temp zip and run unzip -t / unzip -d against it on the server to see the concrete entry-level error.
- If unzip also fails, the artifact is bad - report that plugin/version to Leantime.
- Clean the half-extracted folder before retrying so error 82 does not mask the original failure.
Defensive patterns
Strategy: validation
Validate before calling
// preflight: space for the uncompressed payload and a writable destination
if (disk_free_space($this->pluginDirectory) < $zipUncompressedBytes) {
throw new RuntimeException('Insufficient disk space to extract plugin');
}
if (! is_writable($this->pluginDirectory)) {
throw new RuntimeException('Plugin directory not writable - extraction will fail');
} Try / catch
catch (\Exception $e) {
if ($e->getMessage() === __('notification.plugin_zip_cant_extract')) {
// half-extracted dir must be removed or the next attempt fails elsewhere
File::deleteDirectory($pluginDir);
return back()->with('error', 'Extraction failed - check disk space and permissions, then retry.');
}
throw $e;
} Prevention
- Reserve disk for the uncompressed size, not just the zip.
- Verify artifacts with unzip -t when installs fail repeatedly.
- Always clean the partially extracted folder after a failed extraction.
When it happens
Trigger: installMarketplacePlugin() where extraction into app/Plugins/{Folder} fails: the PHP user cannot write into the just-created directory (umask/ownership edge cases), the disk fills mid-extract (uncompressed size exceeds free space), the zip uses an unsupported compression (e.g. Deflate64/PPMd), or individual entries fail CRC.
Common situations: Disk space exhausted by the uncompressed plugin (asset-heavy plugins); marketplace artifacts produced with non-standard compression; permission surprises right after the mkdir; partial extraction leaving a broken half-plugin that then also trips error 82 on the next attempt.
Related errors
- notification.plugin_zip_exists
- notification.plugin_zip_inconsistent
- notification.plugin_zip_invalid_arg
- notification.plugin_zip_malloc
- notification.plugin_zip_no_file
AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21).
Data as JSON: /api/errors/60930104429f9fe3.
Report an issue: GitHub.