{"record":{"id":"0ffa390868ce551f","repo":"PHPOffice/PHPWord","slug":"could-not-close-zip-file","errorCode":null,"errorMessage":"Could not close zip file.","messagePattern":"Could not close zip file\\.","errorType":"exception","errorClass":"PhpOffice\\PhpWord\\Exception\\Exception","httpStatus":null,"severity":"critical","filePath":"src/PhpWord/TemplateProcessor.php","lineNumber":1014,"sourceCode":"     */\n    public function save()\n    {\n        foreach ($this->tempDocumentHeaders as $index => $xml) {\n            $this->savePartWithRels($this->getHeaderName($index), $xml);\n        }\n\n        $this->savePartWithRels($this->getMainPartName(), $this->tempDocumentMainPart);\n        $this->savePartWithRels($this->getSettingsPartName(), $this->tempDocumentSettingsPart);\n\n        foreach ($this->tempDocumentFooters as $index => $xml) {\n            $this->savePartWithRels($this->getFooterName($index), $xml);\n        }\n\n        $this->zipClass->addFromString($this->getDocumentContentTypesName(), $this->tempDocumentContentTypes);\n\n        // Close zip file\n        if (false === $this->zipClass->close()) {\n            throw new Exception('Could not close zip file.'); // @codeCoverageIgnore\n        }\n\n        return $this->tempDocumentFilename;\n    }\n\n    /**\n     * @param string $fileName\n     * @param string $xml\n     */\n    protected function savePartWithRels($fileName, $xml): void\n    {\n        $this->zipClass->addFromString($fileName, $xml);\n        if (isset($this->tempDocumentRelations[$fileName])) {\n            $relsFileName = $this->getRelationsName($fileName);\n            $this->zipClass->addFromString($relsFileName, $this->tempDocumentRelations[$fileName]);\n        }\n    }\n","sourceCodeStart":996,"sourceCodeEnd":1032,"githubUrl":"https://github.com/PHPOffice/PHPWord/blob/aef95c04151b5633cc505f672ddd6a71da900ee1/src/PhpWord/TemplateProcessor.php#L996-L1032","documentation":"TemplateProcessor::save() writes all modified parts back into the temporary zip archive and then closes it. If ZipArchive::close() returns false the archive could not be finalized — typically because the temp file is not writable, the disk is full, or the file is locked. The saved document is invalid or missing.","triggerScenarios":"Calling save()/saveAs() when the temporary document file cannot be written: read-only temp directory, insufficient disk space, file locked by another process (e.g. opened in Word), or the ZipArchive is in an inconsistent state.","commonSituations":"Servers with a full /tmp or read-only storage after deployment changes; concurrent processing of the same template file; antivirus or file-sync tools (OneDrive/Dropbox) locking the output file on Windows.","solutions":["Check disk space and write permissions on the temp/output directory (sys_get_temp_dir() and the saveAs target).","Ensure the output file is not open/locked by another process or held by antivirus/sync software.","Call saveAs($path) to a fully writable path instead of relying on the default temp location.","Retry the save once with a fresh TemplateProcessor instance if it persists."],"exampleFix":"// before\n$path = $processor->save(); // fails on read-only /tmp\n// after\n$processor->saveAs('/var/www/writable/output.docx');","handlingStrategy":"try-catch","validationCode":"function canSaveTo(string $dir): bool {\n    return is_dir($dir) && is_writable($dir) && (disk_free_space($dir) === false || disk_free_space($dir) > 1024 * 1024);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $processor->saveAs($target);\n} catch (\\PhpOffice\\PhpWord\\Exception\\Exception $e) {\n    if (str_contains($e->getMessage(), 'Could not close zip file')) {\n        error_log('Zip close failed: check disk space/locks on ' . $target);\n        $processor = null; // rebuild template processor and retry\n    } else { throw $e; }\n}","preventionTips":["Monitor disk space on the temp directory and output volume.","Never save to files that may be open in Word or held by sync/AV software.","Save to local writable storage, then move to network/cloud storage after closing.","Retry transient failures with a fresh TemplateProcessor instance."],"tags":["phpword","zip","filesystem","io"],"backgroundTag":"file-write-failed","analyzedSha":"aef95c04151b5633cc505f672ddd6a71da900ee1","analyzedAt":"2026-09-14T10:53:58.933Z","contentChangedAt":"2026-09-14T10:53:58.933Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}