Tencent/WeKnora · error

read response body: %w

Error message

read response body: %w

What it means

After MinerU returns HTTP 200, callFileParse reads the full response body with io.ReadAll. If that read fails (connection reset mid-response, context cancellation, truncated chunked transfer, or a broken pipe to the MinerU server), the converter wraps the underlying error with this message. The request succeeded at the HTTP level but the response payload could not be received completely.

Source

Thrown at internal/infrastructure/docparser/mineru_converter.go:259

	client := utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{
		Timeout:      mineruTimeout,
		MaxRedirects: 5,
	})
	resp, err := client.Do(httpReq)
	if err != nil {
		return "", nil, fmt.Errorf("HTTP request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		respBody, _ := io.ReadAll(resp.Body)
		return "", nil, fmt.Errorf("MinerU API status %d: %s", resp.StatusCode, string(respBody))
	}

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", nil, fmt.Errorf("read response body: %w", err)
	}

	// Dump raw response for debugging (truncate if too large)
	rawStr := string(respBody)
	if len(rawStr) > 4000 {
		logger.Infof(context.Background(), "[MinerU] Raw response (truncated to 4000 chars): %s ...", rawStr[:4000])
	} else {
		logger.Infof(context.Background(), "[MinerU] Raw response: %s", rawStr)
	}

	// Also pretty-print the top-level structure (without large base64 blobs)
	var rawMap map[string]interface{}
	if err := json.Unmarshal(respBody, &rawMap); err == nil {
		c.logMinerUResponseStructure(rawMap, "")
	}

	mdContent, imagesB64, resultKey, err := parseMinerUFileParseResponse(respBody, uploadFileName)
	if err != nil {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Retry the parse request — transient resets usually succeed on a second attempt
  2. Increase the client timeout and any proxy idle/read timeouts between the app and MinerU
  3. Check that no reverse proxy (nginx/ingress) is buffering or truncating large responses
  4. Ensure the caller's context is not cancelled prematurely (increase upstream deadline)
  5. Verify network stability/DNS to the MinerU host

Example fix

// before
client := &http.Client{Timeout: 10 * time.Second}
// after
client := &http.Client{Timeout: 300 * time.Second} // allow large MinerU responses to stream
Defensive patterns

Strategy: retry

Validate before calling

client := &http.Client{Timeout: 300 * time.Second}
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
if _, err := client.Do(req); err != nil {
    if errors.Is(err, context.DeadlineExceeded) { /* increase deadline */ }
}

Type guard

func isBodyReadError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "read response body")
}

Try / catch

result, err := callFileParse(ctx, file)
if isBodyReadError(err) {
    result, err = retryWithBackoff(3, callFileParse, ctx, file)
}
if err != nil { return err }

Prevention

When it happens

Trigger: io.ReadAll(resp.Body) in callFileParse returns an error: network interruption after headers, MinerU closing the connection mid-body, context deadline exceeded while streaming a large parsed document, or proxy/gateway cutting the chunked response.

Common situations: Very large documents whose parsed JSON/zip takes long to stream and hits an idle-timeout on a proxy; unstable network to a remote MinerU instance; context canceled because the user request was aborted upstream.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/7c2cf925c17c94d7. Report an issue: GitHub.