{"record":{"id":"694010bd536e1b56","repo":"Tencent/WeKnora","slug":"open-zip-w","errorCode":null,"errorMessage":"open zip: %w","messagePattern":"open zip: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/docparser/mineru_cloud_converter.go","lineNumber":376,"sourceCode":"\t}\n\tclient := utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{Timeout: 120 * time.Second, MaxRedirects: 5})\n\tresp, err := client.Get(zipURL)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"download zip: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", nil, fmt.Errorf(\"download zip status %d\", resp.StatusCode)\n\t}\n\n\tzipData, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"read zip body: %w\", err)\n\t}\n\n\tzr, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"open zip: %w\", err)\n\t}\n\n\t// Find .md files\n\tvar mdFiles []string\n\tentries := make(map[string]*zip.File)\n\tfor _, f := range zr.File {\n\t\tentries[f.Name] = f\n\t\tif strings.HasSuffix(f.Name, \".md\") {\n\t\t\tmdFiles = append(mdFiles, f.Name)\n\t\t}\n\t}\n\tif len(mdFiles) == 0 {\n\t\treturn \"\", nil, fmt.Errorf(\"no .md file found in zip\")\n\t}\n\tsort.Slice(mdFiles, func(i, j int) bool {\n\t\tdi, dj := strings.Count(mdFiles[i], \"/\"), strings.Count(mdFiles[j], \"/\")\n\t\tif di != dj {\n\t\t\treturn di < dj","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/docparser/mineru_cloud_converter.go#L358-L394","documentation":"After downloading the ZIP bytes, downloadAndExtractZip opens them with zip.NewReader. This error means the downloaded payload is not a valid ZIP archive (bad magic bytes, truncated archive, or an HTML/JSON error page served with 200 OK). The download succeeded but the content is not a ZIP.","triggerScenarios":"zip.NewReader(bytes.NewReader(zipData), len(zipData)) fails: MinerU or an intermediary returned a non-ZIP 200 response (e.g. HTML error page from a gateway), or the ZIP was truncated mid-transfer without an io error.","commonSituations":"Reverse proxy returns a 200 HTML 'gateway timeout'/'maintenance' page instead of the ZIP; MinIO/S3 presigned URL expired and returns XML error with 200 via a misconfigured gateway; partial download after connection close without read error.","solutions":["Log the first bytes / size of zipData on failure to see what was actually downloaded (HTML/XML error vs truncated ZIP).","Retry the download; compare Content-Length with actual byte count to detect truncation.","Check the download URL is still valid (presigned URLs / result links can expire after the async job completes).","Ensure the MinerU result ZIP upload itself completed before polling marks the job done."],"exampleFix":"// before\nzr, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))\nif err != nil {\n    return \"\", nil, fmt.Errorf(\"open zip: %w\", err)\n}\n// after\nzr, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))\nif err != nil {\n    return \"\", nil, fmt.Errorf(\"open zip (len=%d, head=%q): %w\", len(zipData), zipData[:min(64, len(zipData))], err)\n}","handlingStrategy":"validation","validationCode":"if len(zipData) < 4 || string(zipData[:2]) != \"PK\" {\n    return fmt.Errorf(\"downloaded payload is not a zip (size=%d)\", len(zipData))\n}","typeGuard":"func isZip(data []byte) bool {\n    return len(data) >= 4 && data[0] == 'P' && data[1] == 'K' && (data[2] == 3 || data[2] == 5 || data[2] == 7)\n}","tryCatchPattern":"md, _, err := conv.Read(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"open zip\") {\n    // inspect/log payload, do not retry blindly; re-trigger the parse job\n    return fmt.Errorf(\"mineru returned non-zip result: %w\", err)\n}","preventionTips":["Validate magic bytes (PK\\x03\\x04) before calling zip.NewReader","Confirm result download URLs haven't expired before fetching","Check gateway/proxy behavior for error pages served with HTTP 200","Verify job completion status before downloading the artifact"],"tags":["zip","corrupt-data","http","parsing"],"backgroundTag":"invalid-zip-archive","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}