{"record":{"id":"537a6fd598aab352","repo":"Leantime/leantime","slug":"notification-plugin-zip-read-err","errorCode":null,"errorMessage":"notification.plugin_zip_read_err","messagePattern":"notification\\.plugin_zip_read_err","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"app/Domain/Plugins/Services/Plugins.php","lineNumber":742,"sourceCode":"        ) {\n            throw new \\Exception(__('notification.plugin_cant_remove'));\n        }\n\n        if (! mkdir($pluginDir) && ! is_dir($pluginDir)) {\n            throw new \\RuntimeException(sprintf('Directory \"%s\" was not created', $pluginDir));\n        }\n\n        $zip = new \\ZipArchive;\n\n        match ($zip->open($temporaryFile)) {\n            \\ZipArchive::ER_EXISTS => throw new \\Exception(__('notification.plugin_zip_exists')),\n            \\ZipArchive::ER_INCONS => throw new \\Exception(__('notification.plugin_zip_inconsistent')),\n            \\ZipArchive::ER_INVAL => throw new \\Exception(__('notification.plugin_zip_invalid_arg')),\n            \\ZipArchive::ER_MEMORY => throw new \\Exception(__('notification.plugin_zip_malloc')),\n            \\ZipArchive::ER_NOENT => throw new \\Exception(__('notification.plugin_zip_no_file')),\n            \\ZipArchive::ER_NOZIP => throw new \\Exception(__('notification.plugin_zip_not_zip')),\n            \\ZipArchive::ER_OPEN => throw new \\Exception(__('notification.plugin_zip_cant_open')),\n            \\ZipArchive::ER_READ => throw new \\Exception(__('notification.plugin_zip_read_err')),\n            \\ZipArchive::ER_SEEK => throw new \\Exception(__('notification.plugin_zip_seek_err')),\n            default => throw new \\Exception(__('notification.plugin_zip_unknown_err')),\n            true => null,\n        };\n\n        if (! $zip->extractTo($pluginDir)) {\n            throw new \\Exception(__('notification.plugin_zip_cant_extract'));\n        }\n\n        $zip->close();\n\n        unlink($temporaryFile);\n\n        // read the composer.json content from the plugin phar file\n        $pluginModel = $this->createPluginFromComposer($foldername, $plugin->license);\n\n        if (! $this->pluginRepository->addPlugin($pluginModel)) {\n            throw new \\Exception(__('notification_cant_add_to_db'));","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/Leantime/leantime/blob/9a9f49f1008f4782b30f6723c54228f4f992e636/app/Domain/Plugins/Services/Plugins.php#L724-L760","documentation":"The match maps ZipArchive::ER_READ to 'Zip: Read error'. libzip hit an I/O read failure while parsing the archive structure out of the temp file - typically because the file is shorter than its internal structures claim (premature EOF during read) or the storage layer failed mid-read. Like ER_INCONS this indicates a damaged/incomplete body, but detected at read time rather than the consistency check.","triggerScenarios":"installMarketplacePlugin() where the temp zip is shorter than the response claimed: a truncated chunked transfer that still ended 'successfully', a tmpfs that silently dropped data when full, or real disk I/O errors on the underlying volume.","commonSituations":"Marketplace/CDN serving short bodies intermittently; /tmp tmpfs hitting its size cap during the write; failing disks on self-hosted instances.","solutions":["Compare the temp file size with the response Content-Length header (log both during install) to confirm truncation.","Retry the install - transient truncation from the marketplace is the most common cause.","Check tmpfs/disk capacity and health: df -h; dmesg | grep -i error.","If reproducible, download manually with curl (same license headers) and verify against the marketplace artifact size."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"$declared = (int) $response->header('Content-Length');\n$actual = strlen($response->body());\nif ($declared > 0 && $actual !== $declared) {\n    throw new RuntimeException(\"Short download ({$actual}/{$declared} bytes) - retry\");\n}","typeGuard":null,"tryCatchPattern":"catch (\\Exception $e) {\n    if ($e->getMessage() === __('notification.plugin_zip_read_err')) {\n        // I/O read failure on a short body: refetch once before giving up\n        return retryInstall($plugin, $version, attempts: 2);\n    }\n    throw $e;\n}","preventionTips":["Compare body length with Content-Length immediately after download.","Keep temp storage healthy and roomy; watch dmesg for disk errors.","Treat read errors on freshly downloaded files as transient first, systematic second."],"tags":["plugins","ziparchive","io","truncated-download"],"backgroundTag":"truncated-download","analyzedSha":"9a9f49f1008f4782b30f6723c54228f4f992e636","analyzedAt":"2026-08-21T02:37:38.966Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}