Tencent/WeKnora · error

read response body: %w

Error message

read response body: %w

What it means

After a successful HTTP response, doRequest reads the whole body with io.ReadAll. If reading fails (connection reset mid-body, premature close, context deadline during read), it wraps the error as "read response body: %w" and, if retries remain, backs off and retries the request.

Source

Thrown at internal/datasource/connector/yuque/client.go:91

			logger.Infof(ctx, "[Yuque] %s %s (retry %d/%d)", method, path, attempt, maxRetries)
		}

		resp, err := c.httpClient.Do(req)
		if err != nil {
			lastErr = fmt.Errorf("execute request: %w", err)
			if attempt < maxRetries {
				if sErr := sleepCtx(ctx, backoff[attempt]); sErr != nil {
					return sErr
				}
				continue
			}
			return lastErr
		}

		body, readErr := io.ReadAll(resp.Body)
		resp.Body.Close()
		if readErr != nil {
			lastErr = fmt.Errorf("read response body: %w", readErr)
			if attempt < maxRetries {
				if sErr := sleepCtx(ctx, backoff[attempt]); sErr != nil {
					return sErr
				}
				continue
			}
			return lastErr
		}

		bodyPreview := truncate(string(body), 500)
		logger.Infof(ctx, "[Yuque] %s %s → status=%d bodyLen=%d body=%s",
			method, path, resp.StatusCode, len(body), bodyPreview)

		if resp.StatusCode == 429 {
			wait := parseRetryAfter(resp.Header.Get("Retry-After"), backoff[min(attempt, len(backoff)-1)])
			lastErr = fmt.Errorf("yuque rate limited: status=429 body=%s", bodyPreview)
			if attempt < maxRetries {
				if sErr := sleepCtx(ctx, wait); sErr != nil {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Retry — the client already retries automatically; check if retries were exhausted and investigate network stability.
  2. Increase the context/request timeout so large bodies can be fully read.
  3. Check intermediary proxies/LBs for connection-reset or idle-timeout issues.
  4. Verify stable connectivity to www.yuque.com (packet loss, MTU issues).

Example fix

// before
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
// after
ctx, cancel := context.WithTimeout(ctx, 30*time.Second) // allow full body read
Defensive patterns

Strategy: retry

Type guard

func isBodyReadError(err error) bool {
    return strings.Contains(err.Error(), "read response body:")
}

Try / catch

err := client.ListBookDocs(ctx, repo, page)
if err != nil && strings.Contains(err.Error(), "read response body:") {
    // transient network issue; client already retried — back off and retry at job level
    return err
}

Prevention

When it happens

Trigger: The server or an intermediary (LB/proxy) closes the connection before the full body arrives; network interruption mid-transfer; context deadline exceeded while streaming a large response.

Common situations: Unstable networks or flaky VPNs; very large API responses hitting a short context timeout; load balancers with aggressive idle timeouts; TLS interception proxies resetting connections.

Related errors


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