Leantime/leantime · warning · Exception
notification.plugin_zip_seek_err
Error message
notification.plugin_zip_seek_err
What it means
The match maps ZipArchive::ER_SEEK to 'Zip: Seek error'. libzip could not seek within the temp file while locating the central directory (it seeks backward from EOF). A regular file on a healthy local filesystem always supports this, so the error points at an exotic storage layer for /tmp - certain FUSE, network, or overlay mounts - or a file changed under libzip between operations.
Source
Thrown at app/Domain/Plugins/Services/Plugins.php:743
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();
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'));
}View on GitHub (pinned to 9a9f49f100)
Solutions
- Point sys_temp_dir at a local ext4/xfs volume (php.ini sys_temp_dir, or TMPDIR for the php-fpm service) and retry.
- Check what /tmp actually is: mount | grep ' /tmp ' - replace network/overlay mounts with local storage.
- Ensure only one install of the same plugin runs at a time.
- Retry on a different host to confirm it is storage-specific.
Defensive patterns
Strategy: fallback
Validate before calling
// ensure the temp dir sits on a seekable local filesystem
$dir = sys_get_temp_dir();
if (str_contains(realpath($dir) ?: '', '/nfs') || is_link($dir)) {
$dir = '/var/tmp'; // local fallback
if (! is_writable($dir)) {
throw new RuntimeException('No local seekable temp dir available');
}
} Prevention
- Keep /tmp on local ext4/xfs storage, not network or exotic FUSE mounts.
- Set sys_temp_dir explicitly on virtualized/shared hosting.
- Serialize installs of the same plugin to avoid concurrent temp-file mutation.
When it happens
Trigger: installMarketplacePlugin() with sys_get_temp_dir() resolving to an NFS/overlay/FUSE mount whose seek/mmap semantics are broken; the temp file being modified concurrently (another process appending/truncating) while ZipArchive parses it.
Common situations: /tmp mounted over network storage in virtualized hosting; exotic container storage drivers; concurrent duplicate installs racing on the same temp filename.
Related errors
- notification.plugin_zip_no_file
- notifications.plugin_install_cant_find_composer
- notification.plugin_cant_download
- notification.plugin_cant_remove
- Directory "%s" was not created
AI-assisted analysis of Leantime/leantime@9a9f49f100 (2026-08-21).
Data as JSON: /api/errors/6dfbf144defc01cc.
Report an issue: GitHub.