actix/actix-web · error
cannot reuse response builder
Error message
cannot reuse response builder
What it means
This is a runtime panic from the actix-http `ResponseBuilder::message_body()` method. The builder uses an `Option<BoxedResponseHead>` internally; calling `message_body()` (or `body()`, `finish()`) takes the head via `self.head.take()` at line 223, leaving it as `None`. A second call to any body-producing method finds `None` and panics via `.expect("cannot reuse response builder")`.
Solutions
- Call `.body()` or `.finish()` exactly once per builder instance
- Use `ResponseBuilder::take()` to extract the builder state into a fresh instance if you need to split the flow
- Return the response immediately after building it rather than reusing the builder
- Use `match` or `if/else` that produces a single `.body()` call at the end of the function
Example fix
// before let mut builder = ResponseBuilder::new(StatusCode::OK); let res1 = builder.finish(); let res2 = builder.finish(); // panics // after let res = ResponseBuilder::new(StatusCode::OK).finish();
Defensive patterns
Strategy: validation
Validate before calling
// Track builder usage to prevent reuse. Prefer functional construction.
// Always build in a single chain to guarantee one terminal call:
fn build_response(status: actix_http::StatusCode) -> actix_http::Response<actix_http::body::EitherBody<()>> {
ResponseBuilder::new(status)
.insert_header(("X-Test", "1"))
.finish()
} Try / catch
// Rust does not catch panics in normal code.
// Ensure .body()/.finish() is called exactly once.
// If you need conditional logic, structure it so only one terminal call runs:
let res = if condition {
builder.body("data")
} else {
builder.finish()
};
// Do NOT call any builder method after this point. Prevention
- Call .body(), .finish(), or .message_body() exactly once per ResponseBuilder
- Use method chaining to prevent accidental reuse: ResponseBuilder::new(status).header(...).finish()
- Return the built response immediately; do not store and reuse the builder
- Use ResponseBuilder::take() if you need to split builder state across code paths
When it happens
Trigger: Calling `.body(...)` or `.finish()` twice on the same `ResponseBuilder`, or calling `.body(...)` after `.finish()` (since `finish()` internally calls `body(())`). Also panics if calling any method that accesses `self.inner()` after the head has been taken.
Common situations: Storing a `ResponseBuilder` in a variable and conditionally calling `.body()` in different branches where both might execute. Most commonly happens with mutable references shared across code paths.
Related errors
- cannot reuse response builder
- actix-http client only supports versions http/1.1 & http/2
- All default headers must be added before cloning.
- Invalid header name
- Invalid header value
AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09).
Data as JSON: /api/errors/bf42cc32d0fbae98.
Report an issue: GitHub.
Appendix: source
Thrown at actix-http/src/responses/builder.rs:223
pub fn body<B>(&mut self, body: B) -> Response<EitherBody<B>>
where
B: MessageBody + 'static,
{
match self.message_body(body) {
Ok(res) => res.map_body(|_, body| EitherBody::left(body)),
Err(err) => Response::from(err).map_body(|_, body| EitherBody::right(body)),
}
}
/// Generate response with a body.
///
/// This `ResponseBuilder` will be left in a useless state.
pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> {
if let Some(err) = self.err.take() {
return Err(Error::new_http().with_cause(err));
}
let head = self.head.take().expect("cannot reuse response builder");
Ok(Response {
head,
body,
extensions: RefCell::new(Extensions::new()),
})
}
/// Generate response with an empty body.
///
/// This `ResponseBuilder` will be left in a useless state.
#[inline]
pub fn finish(&mut self) -> Response<EitherBody<()>> {
self.body(())
}
/// Create an owned `ResponseBuilder`, leaving the original in a useless state.
pub fn take(&mut self) -> ResponseBuilder {View on GitHub (pinned to 4d435abc28)