actix/actix-web · error · PayloadError
Response Payload IO timed out
Error message
Response Payload IO timed out
What it means
This is a runtime I/O error (`PayloadError::Io` with `ErrorKind::TimedOut`) raised when the client response body payload read exceeds the configured timeout. The `ResponseTimeout::poll_timeout` method at line 33-47 checks if the sleep timer has elapsed during body reading and returns this error to signal that the server took too long to send response data.
Solutions
- Increase or disable the response timeout: `client.get(url).timeout(Duration::from_secs(60)).send()`
- Set a global client timeout when building the client: `Client::builder().timeout(Duration::from_secs(120)).finish()`
- If the server is genuinely slow, consider streaming the body in chunks with explicit backpressure handling
- Verify the server is not hanging or deadlocked — the timeout may be correctly catching a broken upstream
Example fix
// before
let mut resp = client.get(url).send().await?;
let body = resp.body().await?; // times out on slow server
// after
let mut resp = client.get(url)
.timeout(Duration::from_secs(120))
.send().await?;
let body = resp.body().await?; Defensive patterns
Strategy: try-catch
Validate before calling
// Set an appropriate timeout based on expected response time.
// For large/slow responses, increase or disable the timeout.
let resp = client.get(url)
.timeout(Duration::from_secs(120))
.send()
.await?; Try / catch
// Match on PayloadError to handle timeout gracefully.
use awc::error::SendRequestError;
match client.get(url).timeout(Duration::from_secs(30)).send().await {
Ok(mut resp) => {
match resp.body().await {
Ok(body) => { /* process body */ }
Err(awc::error::PayloadError::Io(e)) if e.kind() == std::io::ErrorKind::TimedOut => {
// Handle timeout: retry, fallback, or return error
eprintln!("Response body timed out");
}
Err(e) => { /* other payload error */ }
}
}
Err(e) => { /* connection or request error */ }
} Prevention
- Set a generous timeout for slow endpoints: .timeout(Duration::from_secs(120))
- Consider streaming large responses instead of reading the entire body at once
- Monitor server response times to choose an appropriate timeout value
- Implement retry logic with backoff for transient network slowness
When it happens
Trigger: Calling `.send()` on an awc client request and then reading the response body (e.g., `.body()`, `.json()`) when the server is slow to send body chunks. The timeout is set via `ClientRequest::timeout()` or the client's default configuration.
Common situations: Downloading large files from a slow server, server-side streaming endpoints that trickle data, or network issues causing stalls. Also common when the default timeout is too short for the workload.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- actix-http client only supports versions http/1.1 & http/2
- All default headers must be added before cloning.
- argument to scope macro is not a string literal, expected…
- cannot reuse response builder
- cannot reuse response builder
AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09).
Data as JSON: /api/errors/a28553555af33893.
Report an issue: GitHub.
Appendix: source
Thrown at awc/src/responses/mod.rs:37
///
/// See [`ClientResponse::_timeout`] for reason.
pub(crate) enum ResponseTimeout {
Disabled(Option<Pin<Box<Sleep>>>),
Enabled(Pin<Box<Sleep>>),
}
impl Default for ResponseTimeout {
fn default() -> Self {
Self::Disabled(None)
}
}
impl ResponseTimeout {
fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> {
match *self {
Self::Enabled(ref mut timeout) => {
if timeout.as_mut().poll(cx).is_ready() {
Err(PayloadError::Io(io::Error::new(
io::ErrorKind::TimedOut,
"Response Payload IO timed out",
)))
} else {
Ok(())
}
}
Self::Disabled(_) => Ok(()),
}
}
}
View on GitHub (pinned to 4d435abc28)