{"record":{"id":"0f83e5378ed8225a","repo":"Pumpkin-MC/Pumpkin","slug":"failed-to-read-http-response-body-0","errorCode":null,"errorMessage":"Failed to read HTTP response body: {0}","messagePattern":"Failed to read HTTP response body: (.+?)","errorType":"exception","errorClass":"HttpError","httpStatus":null,"severity":"error","filePath":"crates/pumpkin-plugin-utils/src/http.rs","lineNumber":15,"sourceCode":"//! HTTP client helpers for online license checks and marketplace queries.\n\nuse thiserror::Error;\n\n/// HTTP request errors.\n#[derive(Debug, Error)]\npub enum HttpError {\n    /// Network or connection error.\n    #[error(\"HTTP request failed: {0}\")]\n    RequestFailed(String),\n    /// Response status code was not successful (2xx).\n    #[error(\"HTTP response returned error status {0}: {1}\")]\n    BadStatus(u16, String),\n    /// Error reading response body.\n    #[error(\"Failed to read HTTP response body: {0}\")]\n    BodyRead(String),\n}\n\n/// Helper client for querying Pumpkin marketplace REST APIs.\npub struct HttpClient {\n    client: reqwest::blocking::Client,\n}\n\nimpl Default for HttpClient {\n    fn default() -> Self {\n        Self::new(\"Pumpkin-Plugin-Utils/0.1.0\")\n    }\n}\n\nimpl HttpClient {\n    /// Creates a new HTTP client with the specified User-Agent header.\n    #[must_use]\n    pub fn new(user_agent: &str) -> Self {","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin-plugin-utils/src/http.rs#L1-L33","documentation":"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}.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the request once; BodyRead is often transient (dropped connection).","Increase or verify the reqwest client's read/total timeout in HttpClient configuration.","Check network/proxy stability (curl the marketplace endpoint to confirm full body arrives).","Inspect the embedded reqwest error ({0}) for 'timed out' vs 'connection reset' to target the cause.","Pin/verify TLS and compression features match the server (disable optional gzip feature if decompression errors persist)."],"exampleFix":"// before\nlet body = resp.text()?; // surfaces BodyRead on failure\n// after\nlet body = match resp.text() {\n    Ok(b) => b,\n    Err(e) => { tracing::warn!(\"body read failed, retrying: {e}\"); resp2.text()? }\n};","handlingStrategy":"retry","validationCode":"// Best-effort pre-check that the endpoint is reachable before issuing the real call\nif let Err(e) = std::net::TcpStream::connect_timeout(\n    &marketplace_addr, std::time::Duration::from_secs(5)) {\n    tracing::warn!(\"marketplace unreachable, skipping call: {e}\");\n    return Ok(None);\n}","typeGuard":"fn is_body_read(e: &HttpError) -> bool {\n    matches!(e, HttpError::BodyRead(_))\n}","tryCatchPattern":"match client.get(url).send().and_then(|r| r.text()) {\n    Ok(body) => parse(body),\n    Err(HttpError::BodyRead(e)) => {\n        tracing::warn!(\"transient body read failure: {e}\");\n        backoff_retry(|| fetch(), 3)\n    }\n    Err(e) => Err(e.into()),\n}","preventionTips":["Set a generous read_timeout on the reqwest blocking client.","Retry idempotent GETs with exponential backoff.","Avoid proxies/interceptors that truncate responses.","Monitor the embedded error string to distinguish timeouts vs resets."],"tags":["http","network","reqwest","rust"],"backgroundTag":"http-request-failed","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}