Pumpkin-MC/Pumpkin · error · HttpError

Failed to read HTTP response body

Error message

Failed to read HTTP response body: {0}

What it means

HttpError::BodyRead from pumpkin-plugin-utils' HttpClient. It means an HTTP request to a marketplace API succeeded at the transport level, but the response body could not be read into a string/buffer (connection dropped mid-body, timeout while streaming, decompression failure, etc.). reqwest's `.text()`/`.bytes()` failed and the underlying error string is embedded via {0}.

Solutions

  1. Retry the request once; BodyRead is often transient (dropped connection).
  2. Increase or verify the reqwest client's read/total timeout in HttpClient configuration.
  3. Check network/proxy stability (curl the marketplace endpoint to confirm full body arrives).
  4. Inspect the embedded reqwest error ({0}) for 'timed out' vs 'connection reset' to target the cause.
  5. Pin/verify TLS and compression features match the server (disable optional gzip feature if decompression errors persist).

Example fix

// before
let body = resp.text()?; // surfaces BodyRead on failure
// after
let body = match resp.text() {
    Ok(b) => b,
    Err(e) => { tracing::warn!("body read failed, retrying: {e}"); resp2.text()? }
};
Defensive patterns

Strategy: retry

Validate before calling

// Best-effort pre-check that the endpoint is reachable before issuing the real call
if let Err(e) = std::net::TcpStream::connect_timeout(
    &marketplace_addr, std::time::Duration::from_secs(5)) {
    tracing::warn!("marketplace unreachable, skipping call: {e}");
    return Ok(None);
}

Type guard

fn is_body_read(e: &HttpError) -> bool {
    matches!(e, HttpError::BodyRead(_))
}

Try / catch

match client.get(url).send().and_then(|r| r.text()) {
    Ok(body) => parse(body),
    Err(HttpError::BodyRead(e)) => {
        tracing::warn!("transient body read failure: {e}");
        backoff_retry(|| fetch(), 3)
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling any HttpClient method (e.g. marketplace license or update queries) where reqwest::blocking::Response::text()/bytes() returns Err: server closes connection before sending full body, read timeout elapses mid-download, chunked encoding interrupted, or decompression (gzip/brotli) of the body fails.

Common situations: Unstable network or proxy between the plugin host and the marketplace; marketplace server crashes mid-response; overly short reqwest read_timeout; intercepting proxies/antivirus truncating responses; very large bodies with socket resets.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09). Data as JSON: /api/errors/0f83e5378ed8225a. Report an issue: GitHub.

Appendix: source

Thrown at crates/pumpkin-plugin-utils/src/http.rs:15

//! HTTP client helpers for online license checks and marketplace queries.

use thiserror::Error;

/// HTTP request errors.
#[derive(Debug, Error)]
pub enum HttpError {
    /// Network or connection error.
    #[error("HTTP request failed: {0}")]
    RequestFailed(String),
    /// Response status code was not successful (2xx).
    #[error("HTTP response returned error status {0}: {1}")]
    BadStatus(u16, String),
    /// Error reading response body.
    #[error("Failed to read HTTP response body: {0}")]
    BodyRead(String),
}

/// Helper client for querying Pumpkin marketplace REST APIs.
pub struct HttpClient {
    client: reqwest::blocking::Client,
}

impl Default for HttpClient {
    fn default() -> Self {
        Self::new("Pumpkin-Plugin-Utils/0.1.0")
    }
}

impl HttpClient {
    /// Creates a new HTTP client with the specified User-Agent header.
    #[must_use]
    pub fn new(user_agent: &str) -> Self {

View on GitHub (pinned to 8d4639e25a)