{"record":{"id":"e10cdc6f83230da8","repo":"Tencent/WeKnora","slug":"read-zip-body-w","errorCode":null,"errorMessage":"read zip body: %w","messagePattern":"read zip body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/docparser/mineru_cloud_converter.go","lineNumber":371,"sourceCode":"var imgRefPattern = regexp.MustCompile(`!\\[[^\\]]*\\]\\(([^)]+)\\)`)\n\nfunc downloadAndExtractZip(zipURL string) (string, []types.ImageRef, error) {\n\tif err := utils.ValidateURLForSSRF(zipURL); err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"zip URL blocked by SSRF check: %v\", err)\n\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\")","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/docparser/mineru_cloud_converter.go#L353-L389","documentation":"downloadAndExtractZip downloads the result ZIP produced by an async MinerU parsing job and reads it fully into memory with io.ReadAll. This error wraps any failure while reading the HTTP response body (network reset mid-transfer, timeout, context cancellation, truncated connection). It does not indicate the ZIP itself is corrupt — only that the raw bytes could not be downloaded.","triggerScenarios":"The HTTP GET that fetches the finished job's ZIP returns 200 but the response body fails during io.ReadAll(resp.Body): connection reset, unexpected EOF, context deadline exceeded while streaming a large ZIP.","commonSituations":"Large result ZIPs from big PDFs dropped by proxies/load balancers with idle-timeout limits; caller context canceled because the user request timed out upstream; unstable network between the service and the MinerU result storage endpoint.","solutions":["Retry downloadAndExtractZip with backoff — transient network failures usually succeed on a second attempt.","Check the caller's ctx deadline; increase it if the ZIP is large relative to the timeout.","Verify any proxy/load balancer between the service and MinerU allows large streaming responses (raise idle/response timeouts, disable response buffering limits).","Check upstream logs for the wrapped cause (%w) to distinguish reset vs timeout vs cancellation."],"exampleFix":"// before\nzipData, err := io.ReadAll(resp.Body)\nif err != nil {\n    return \"\", nil, fmt.Errorf(\"read zip body: %w\", err)\n}\n// after\nzipData, err := io.ReadAll(resp.Body)\nif err != nil {\n    if ctx.Err() != nil {\n        return \"\", nil, fmt.Errorf(\"read zip body: canceled: %w\", ctx.Err())\n    }\n    return \"\", nil, fmt.Errorf(\"read zip body: %w\", err) // retryable\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"md, _, err := conv.Read(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"read zip body\") {\n    // transient network: retry with backoff, respecting ctx\n    md, _, err = conv.Read(ctx, req)\n}","preventionTips":["Set generous context deadlines sized to expected ZIP sizes","Retry idempotent download operations with exponential backoff","Monitor proxy/load-balancer response-size and timeout limits","Check ctx.Err() after failures to distinguish cancellation from network errors"],"tags":["network","http","io","zip-download"],"backgroundTag":"http-response-read-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}