{"record":{"id":"15a67c944ab74bb3","repo":"BookStackApp/BookStack","slug":"failed-to-load-extract-files-from-module-zip-with","errorCode":null,"errorMessage":"Failed to load extract files from module ZIP with error: {$exception->getMessage()}","messagePattern":"Failed to load extract files from module ZIP with error: (.+?)","errorType":"exception","errorClass":"ThemeModuleException","httpStatus":null,"severity":"error","filePath":"app/Theming/ThemeModuleManager.php","lineNumber":60,"sourceCode":"    /**\n     * @throws ThemeModuleException\n     */\n    public function addFromZip(string $name, ThemeModuleZip $zip): ThemeModule\n    {\n        $baseFolderName = Str::limit(Str::slug($name), 40, '');\n        $folderName = $baseFolderName;\n        while (!$baseFolderName || file_exists($this->modulesFolderPath . DIRECTORY_SEPARATOR . $folderName)) {\n            $folderName = ($baseFolderName ?: 'mod') . '-' . Str::random(4);\n        }\n\n        $folderPath = $this->modulesFolderPath . DIRECTORY_SEPARATOR . $folderName;\n        try {\n            $zip->extractTo($folderPath);\n        } catch (ThemeModuleException $exception) {\n            if (is_dir($folderPath)) {\n                $this->deleteDirectoryRecursively($folderPath);\n            }\n            throw new ThemeModuleException(\"Failed to load extract files from module ZIP with error: {$exception->getMessage()}\");\n        }\n\n        $module = $this->loadFromFolder($folderName);\n        if (!$module) {\n            throw new ThemeModuleException(\"Failed to load module from zip file after extraction\");\n        }\n\n        return $module;\n    }\n\n    protected function deleteDirectoryRecursively(string $path): void\n    {\n        $items = array_diff(scandir($path), ['.', '..']);\n        foreach ($items as $item) {\n            $itemPath = $path . DIRECTORY_SEPARATOR . $item;\n            if (is_dir($itemPath)) {\n                $this->deleteDirectoryRecursively($itemPath);\n            } else {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Theming/ThemeModuleManager.php#L42-L78","documentation":"ThemeModuleManager::addFromZip wraps failures from $zip->extractTo(): if extraction of a module ZIP throws, the partially extracted folder is removed and a new ThemeModuleException is thrown embedding the original error message. It indicates the ZIP contents could not be written to disk under the themes modules folder.","triggerScenarios":"Installing a module via ThemeModuleZip::extractTo where the zip is corrupt/encrypted, an entry name is unsafe, the target path isn't writable, or disk space is exhausted. Raised through the 'handle' upload flow.","commonSituations":"Truncated or re-saved ZIP downloads; read-only storage directory after deployment/permission changes; full disk; malicious or malformed zip entries rejected by the extractor; PHP zip extension limitations.","solutions":["Check storage permissions on the themes modules folder (web-server user needs write access) and retry the upload","Re-download or rebuild the ZIP and verify it opens with unzip -t","Free disk space if the volume is full","Read the embedded {$exception->getMessage()} in the message — it names the underlying cause (e.g. specific entry or path)","Confirm the PHP zip extension is installed and current"],"exampleFix":"// before (debugging)\n// message: Failed to load extract files from module ZIP with error: ...\n// after (server check)\nsudo -u www-data test -w themes/modules && echo writable\nunzip -t my-module.zip","handlingStrategy":"try-catch","validationCode":"$zipPath = $uploadedFile->getRealPath();\n$zipArchive = new ZipArchive();\nif ($zipArchive->open($zipPath) !== true || $zipArchive->numFiles === 0) {\n    throw new InvalidArgumentException('ZIP is corrupt or empty');\n}\nif (disk_free_space(dirname($modulesFolderPath)) < 10 * 1024 * 1024) {\n    throw new RuntimeException('Insufficient disk space');\n}\nif (!is_writable($modulesFolderPath)) {\n    throw new RuntimeException('Modules folder not writable');\n}","typeGuard":"function isUsableZip(string $path): bool {\n    $z = new ZipArchive();\n    return $z->open($path) === true && $z->numFiles > 0;\n}","tryCatchPattern":"try {\n    $module = $manager->addFromZip($name, $zip);\n} catch (ThemeModuleException $e) {\n    Log::error('Module ZIP extraction failed', ['reason' => $e->getMessage()]);\n    // surface $e->getMessage() to the uploader; cleanup already handled by manager\n}","preventionTips":["Verify uploaded ZIPs with unzip -t or ZipArchive before installing","Ensure the modules storage folder is writable by the web-server user","Monitor disk space on the storage volume","Keep the PHP zip extension updated; test with real module ZIPs in CI"],"tags":["zip","filesystem","permissions","theming","bookstack"],"backgroundTag":"zip-extraction-failed","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}