AlistGo/alist · error

failed to read response body: %v

Error message

failed to read response body: %v

What it means

io.ReadAll on the response body of POST /upload/resumable.php failed while draining the upload unit acknowledgement. This is a transport-level failure mid-body: the connection dropped after headers were sent, or the body was cut off. The request itself may or may not have been processed server-side, which matters for resumable-upload correctness.

Source

Thrown at drivers/mediafire/util.go:425

	req.ContentLength = int64(len(unitData))

	/* fmt.Printf("Debug resumable upload request:\n")
	fmt.Printf("  URL: %s\n", req.URL.String())
	fmt.Printf("  Headers: %+v\n", req.Header)
	fmt.Printf("  Unit ID: %d\n", unitID)
	fmt.Printf("  Unit Size: %d\n", len(unitData))
	fmt.Printf("  Upload Key: %s\n", uploadKey)
	fmt.Printf("  Action Token: %s\n", actionToken) */

	res, err := base.HttpClient.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		return "", fmt.Errorf("failed to read response body: %v", err)
	}

	//fmt.Printf("MediaFire resumable upload response (status %d): %s\n", res.StatusCode, string(body))

	var uploadResp struct {
		Response struct {
			Doupload struct {
				Key string `json:"key"`
			} `json:"doupload"`
			Result string `json:"result"`
		} `json:"response"`
	}

	if err := json.Unmarshal(body, &uploadResp); err != nil {
		return "", fmt.Errorf("failed to parse response: %v", err)
	}

	if res.StatusCode != 200 {

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Retry the unit upload — the protocol is resumable and bitmap-tracked, so re-sending a unit is safe
  2. Increase the shared HTTP client's timeout and TLS handshake timeout for large unit sizes
  3. If consistently reproducible, capture whether a proxy sits between client and api.mediafire.com and relax its limits
  4. Verify the upload key is still valid before retrying; if expired, re-run uploadCheck to get a new one

Example fix

// before
body, err := io.ReadAll(res.Body)
if err != nil {
    return "", fmt.Errorf("failed to read response body: %v", err)
}

// after
body, err := io.ReadAll(res.Body)
if err != nil {
    return "", fmt.Errorf("failed to read response body (unit %d, status %d): %w", unitID, res.StatusCode, err)
}
Defensive patterns

Strategy: retry

Validate before calling

// Ensure client timeouts accommodate unit-sized transfers
if base.HttpClient.Timeout < time.Duration(unitSize)*time.Millisecond {
    // use a per-request context deadline sized to the unit instead
    ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
    defer cancel()
}

Try / catch

// Safe retry: resumable protocol tolerates re-sent units
err := d.resumableUpload(...)
for attempt := 0; attempt < 3 && isTransient(err); attempt++ {
    time.Sleep(time.Duration(attempt+1) * time.Second)
    err = d.resumableUpload(...)
}

Prevention

When it happens

Trigger: Connection reset/timeout while MediaFire streams the (often large) JSON acknowledgement; proxy or reverse proxy truncating the response; TLS renegotiation mid-body; very slow networks where the client read deadline expires.

Common situations: Mobile or unstable networks uploading large units; corporate proxies with body-size or idle limits; MediaFire server-side connection churn during peak load; misconfigured HTTP client timeouts shorter than body transfer time.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/6d57ac103161d013. Report an issue: GitHub.