getgrav/grav · error · RuntimeException

ZipArchiver: failed to read entry "{name}" from {archive_fil

Error message

ZipArchiver: failed to read entry "{name}" from {archive_file}.

What it means

When the size cap is active, ZipArchiver extracts through extractStreamed(), reading each entry with $zip->getStream($name). If getStream returns false for an entry, the archiver calls abortExtraction(): it closes the zip, deletes the partially-extracted destination folder (so nothing half-written remains), and throws RuntimeException 'ZipArchiver: failed to read entry "<name>" from <archive>'. A single unreadable entry aborts and rolls back the whole extraction.

Source

Thrown at system/src/Grav/Common/Filesystem/ZipArchiver.php:261

            fclose($stream);
        }
    }

    /**
     * Close the archive, remove everything extracted so far, and throw. Keeps a
     * rejected archive from leaving partial output on disk.
     *
     * @param ZipArchive $zip
     * @param string $destination
     * @param string $message
     * @return never
     */
    protected function abortExtraction(ZipArchive $zip, string $destination, string $message): void
    {
        $zip->close();
        Folder::delete($destination);

        throw new RuntimeException($message);
    }

    /**
     * @param string $source
     * @param callable|null $status
     * @return $this
     */
    public function compress($source, ?callable $status = null)
    {
        if (!extension_loaded('zip')) {
            throw new InvalidArgumentException('ZipArchiver: Zip PHP module not installed...');
        }

        // Get real path for our folder
        $rootPath = realpath($source);
        if (!$rootPath) {
            throw new InvalidArgumentException('ZipArchiver: ' . $source . ' cannot be found...');
        }

View on GitHub (pinned to 6040efed04)

Solutions

  1. Test the archive: unzip -t file.zip pinpoints bad entries; re-download or re-create the archive if any fail
  2. If the zip is password-protected, decrypt/re-pack it without a password before feeding it to ZipArchiver (it has no password support)
  3. Check for concurrent modification — do not rebuild the zip while extraction runs
  4. Update the zip extension/libzip on legacy systems to fix per-entry zip64 read failures
Defensive patterns

Strategy: try-catch

Validate before calling

$zip = new ZipArchive();
if ($zip->open($path) === true) {
    for ($i = 0; $i < $zip->count(); $i++) {
        $stat = $zip->statIndex($i);
        if (($stat['encryption_method'] ?? 0) !== 0) {
            $zip->close();
            throw new RuntimeException('Encrypted entries are not supported: ' . $stat['name']);
        }
    }
    $zip->close();
}

Try / catch

try {
    (new ZipArchiver($path))->extract($destination);
} catch (RuntimeException $e) {
    if (str_contains($e->getMessage(), 'failed to read entry')) {
        // destination was auto-rolled back; unzip -t to find the bad entry, re-download/re-pack, retry
    }
}

Prevention

When it happens

Trigger: A corrupt or mismatched central directory (entry declared but data unreadable); password-encrypted entries — getStream() fails for encrypted zips; CRC errors or truncated entry data from an incomplete download; archive modified while being extracted.

Common situations: Partially downloaded GPM packages; password-protected zips uploaded where the workflow expects plain ones; storage-level corruption on uploads; archives created by tools with zip64 quirks that older libzip mishandles per-entry.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/828969d21200a97f. Report an issue: GitHub.