{"record":{"id":"b605881bb14f13af","repo":"jeessy2/ddns-go","slug":"w-zip-s","errorCode":null,"errorMessage":"%w zip 文件: %s","messagePattern":"%w zip 文件: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/update/decompress.go","lineNumber":51,"sourceCode":"\t\t\treturn decompress(src, cmd)\n\t\t}\n\t}\n\tlog.Print(\"It's not a compressed file, skip decompressing\")\n\treturn src, nil\n}\n\nfunc unzip(src io.Reader, cmd string) (io.Reader, error) {\n\t// 解压 Zip 格式时需要文件大小。\n\t// 因此我们需要先将 HTTP 响应读取到缓冲区中。\n\tbuf, err := io.ReadAll(src)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w zip 文件: %v\", errCannotDecompressFile, err)\n\t}\n\n\tr := bytes.NewReader(buf)\n\tz, err := zip.NewReader(r, r.Size())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w zip 文件: %s\", errCannotDecompressFile, err)\n\t}\n\n\tfor _, file := range z.File {\n\t\t_, name := filepath.Split(file.Name)\n\t\tif !file.FileInfo().IsDir() && matchExecutableName(cmd, name) {\n\t\t\treturn file.Open()\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"在 zip 文件中%w：%q\", errExecutableNotFoundInArchive, cmd)\n}\n\nfunc untar(src io.Reader, cmd string) (io.Reader, error) {\n\tgz, err := gzip.NewReader(src)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w tar.gz 文件: %s\", errCannotDecompressFile, err)\n\t}\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/jeessy2/ddns-go/blob/5874c2e6667c69357ab95079421131104bd3fcae/util/update/decompress.go#L33-L69","documentation":"After buffering, unzip opens the data as a zip archive with zip.NewReader; if the bytes are not a valid zip archive, it returns errCannotDecompressFile wrapped with this message and the underlying zip error. It indicates the downloaded content is corrupt or not actually zip format.","triggerScenarios":"Self-update downloads a .zip archive whose bytes fail zip.NewReader — e.g. the URL returned an HTML error page, a partial download, or the asset is actually gzip/tar not zip.","commonSituations":"Release assets replaced or misnamed on the host; CDN/proxy returning an error page with 200; truncated download saved as .zip; pointing the updater at the wrong artifact.","solutions":["Verify the downloaded file is a real zip (file/unzip on disk, or check first bytes for PK magic)","Check the release URL returns the actual zip asset, not an HTML error page","Re-download the asset (truncated downloads commonly corrupt archives); confirm via errors.Is(err, errCannotDecompressFile)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check zip magic bytes before unzipping\nhead := make([]byte, 4)\nif n, _ := io.ReadFull(src, head); n < 4 || string(head[:2]) != \"PK\" {\n    return fmt.Errorf(\"not a zip archive\")\n}","typeGuard":null,"tryCatchPattern":"r, err := unzip(src, cmd)\nif err != nil {\n    if errors.Is(err, errCannotDecompressFile) {\n        log.Errorf(\"downloaded artifact is not a valid zip: %v\", err)\n        return errCorruptAsset\n    }\n    return err\n}","preventionTips":["Verify asset checksums after download before decompressing","Confirm the URL serves the zip asset, not an HTML error page","Check first bytes for the PK magic number","Pin release asset URLs and re-download on corruption"],"tags":["zip","decompression","corrupt-archive"],"backgroundTag":"archive-decompression-failed","analyzedSha":"5874c2e6667c69357ab95079421131104bd3fcae","analyzedAt":"2026-09-03T15:41:16.799Z","contentChangedAt":"2026-09-03T15:41:16.799Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}