Leantime/leantime · warning · Exception
notification.plugin_zip_exists
Error message
notification.plugin_zip_exists
What it means
The match over ZipArchive::open($temporaryFile) maps the ER_EXISTS return code to the translated 'Zip: File already exists'. ER_EXISTS means libzip was asked to create a file that already exists, which only applies when opening with the EXCL flag - the call here passes no flags, so in practice this branch is nearly unreachable with stock libzip. If it does fire, a stale temp file with the same name is interfering.
Source
Thrown at app/Domain/Plugins/Services/Plugins.php:735
) {
throw new \Exception(__('notification.plugin_cant_download'));
}
if (
is_dir($pluginDir = "{$this->pluginDirectory}{$foldername}")
&& ! File::deleteDirectory($pluginDir)
) {
throw new \Exception(__('notification.plugin_cant_remove'));
}
if (! mkdir($pluginDir) && ! is_dir($pluginDir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $pluginDir));
}
$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();
View on GitHub (pinned to 9a9f49f100)
Solutions
- Delete the stale archive: rm /tmp/{Folder}.zip (or clear the temp dir), then retry the install.
- Confirm the exact path by logging $temporaryFile; the name is derived from Content-Disposition and may differ from the plugin identifier.
- Verify the open() call passes no flags; if custom code added EXCL, remove it.
Defensive patterns
Strategy: validation
Validate before calling
// clear a stale temp archive before installing
$tmpZip = sys_get_temp_dir().'/'.Str::studly($plugin->identifier).'.zip';
if (file_exists($tmpZip) && ! @unlink($tmpZip)) {
throw new RuntimeException("Cannot clear stale temp archive {$tmpZip}");
} Prevention
- Clear /tmp/{Folder}.zip between install attempts.
- Do not add ZipArchive::EXCL to the open() call without handling the exists case deliberately.
When it happens
Trigger: ZipArchive::open() on /tmp/{Folder}.zip returning ER_EXISTS: a leftover zip from a previous failed install combined with flag semantics (custom builds/older libzip), or a patched call passing ZipArchive::EXCL.
Common situations: Leftover /tmp/{Folder}.zip from an earlier interrupted install; environments where the temp file persists across attempts; forks that add EXCL to avoid overwriting.
Related errors
- notification.plugin_zip_inconsistent
- notification.plugin_zip_malloc
- notification.plugin_zip_not_zip
- notification.plugin_zip_unknown_err
- notification.plugin_cant_download
AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21).
Data as JSON: /api/errors/5a5759de49530c60.
Report an issue: GitHub.