{"record":{"id":"91f8be4dcd4e7254","repo":"getgrav/grav","slug":"ziparchiver-zip-failed-to-extract-archive-file","errorCode":null,"errorMessage":"ZipArchiver: ZIP failed to extract {archive_file} to {destination}","messagePattern":"ZipArchiver: ZIP failed to extract (.+?) to (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Common/Filesystem/ZipArchiver.php","lineNumber":104,"sourceCode":"                        $totalSize += (int) $stat['size'];\n                        if ($totalSize > $maxSize) {\n                            $zip->close();\n                            throw new RuntimeException('ZipArchiver: refused to extract ' . $this->archive_file . '. Archive exceeds the maximum uncompressed size (' . $maxSize . ' bytes).');\n                        }\n                    }\n                }\n            }\n\n            Folder::create($destination);\n\n            if ($maxSize > 0) {\n                // Enforce the uncompressed-size cap against bytes actually written,\n                // so a forged-small declared size cannot smuggle a bomb past the\n                // advisory pre-pass above (GHSA-8h9x-89f2-m7x3).\n                $this->extractStreamed($zip, $destination, $numFiles, $maxSize);\n            } elseif (!$zip->extractTo($destination)) {\n                $zip->close();\n                throw new RuntimeException('ZipArchiver: ZIP failed to extract ' . $this->archive_file . ' to ' . $destination);\n            }\n\n            $zip->close();\n\n            return $this;\n        }\n\n        throw new RuntimeException('ZipArchiver: Failed to open ' . $this->archive_file);\n    }\n\n    /**\n     * Resolve the uncompressed-size / file-count / nesting-depth caps applied\n     * before extraction. Shares the same config keys and defaults as\n     * GPM\\Installer so both ZIP extraction paths enforce identical limits. A\n     * limit of 0 disables that particular check.\n     *\n     * @return array{0:int,1:int,2:int} [maxUncompressedBytes, maxFiles, maxDepth]\n     */","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Common/Filesystem/ZipArchiver.php#L86-L122","documentation":"When the uncompressed-size cap is disabled (maxSize = 0), ZipArchiver::extract() falls back to the native ZipArchive::extractTo($destination); if that call returns false, the archive is closed and RuntimeException 'ZipArchiver: ZIP failed to extract <file> to <destination>' is thrown. This is a raw extraction failure — permission, space, or archive-integrity level — with no further detail because PHP's ZipArchive does not expose one here.","triggerScenarios":"Destination directory not writable by the PHP process; disk full during extraction; archive corrupted mid-file so extractTo aborts; extracting into a path blocked by open_basedir; concurrent extraction into the same destination clobbering entries.","commonSituations":"Plugin/theme install into user/ when the web user lacks write permission; low-disk VPS deployments; truncated downloads of package zips; two simultaneous installs targeting the same folder.","solutions":["Verify writability of the destination and free space: is_writable($dest), df -h","Test the archive independently: unzip -t package.zip to detect corruption; re-download if it fails","Re-run the extraction once the environment issue (space/permissions/lock) is fixed","If you need size-cap enforcement too, keep max_uncompressed_size > 0 so the streamed path with better errors is used"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!is_dir($destination) || !is_writable($destination)) {\n            throw new RuntimeException(\"Destination {$destination} missing or not writable\");\n}\nif (disk_free_space($destination) < $expectedSize) {\n    throw new RuntimeException('Insufficient disk space for extraction');\n}","typeGuard":null,"tryCatchPattern":"try {\n    (new ZipArchiver($path))->extract($destination);\n} catch (RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'failed to extract')) {\n        // ZipArchive::extractTo returned false: check perms/space, verify with unzip -t, then retry once\n    }\n}","preventionTips":["Pre-create and chmod destination directories before extraction","Monitor disk space on install endpoints — extractTo fails silently on ENOSPC","Keep max_uncompressed_size > 0 so the streamed extractor (with abort/rollback) is used instead of raw extractTo"],"tags":["grav","zip","extraction","permissions","disk-full"],"backgroundTag":"zip-extraction-failed","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}