actix/actix-web · error

cannot reuse response builder

Error message

cannot reuse response builder

What it means

This is a runtime panic from `HttpResponseBuilder::extensions()`. The builder stores the response in an `Option<Response<BoxBody>>`; after a terminal method like `.body()`, `.finish()`, `.json()`, or `.streaming()` consumes the inner response via `.take()`, calling `.extensions()` finds `None` and panics with `.expect("cannot reuse response builder")` at line 272.

Solutions

  1. Access `.extensions()` before calling any terminal method (`.body()`, `.finish()`, etc.)
  2. Use `.take()` to split the builder into a new instance if you need to access state after building
  3. Return the response immediately after building; do not continue to use the builder
  4. Access extensions on the built `HttpResponse` instead: `res.extensions()`

Example fix

// before
let mut builder = HttpResponse::Ok();
builder.insert_header(("X-Test", "1"));
let res = builder.finish();
let ext = builder.extensions(); // panics

// after
let mut builder = HttpResponse::Ok();
builder.insert_header(("X-Test", "1"));
let ext = builder.extensions(); // before finish
let res = builder.finish();
Defensive patterns

Strategy: validation

Validate before calling

// Access extensions before calling any terminal method.
// Structure handlers to call .finish()/.body() last.
async fn handler(req: HttpRequest) -> HttpResponse {
    let mut builder = HttpResponse::Ok();
    builder.extensions_mut().insert(MyData { id: 42 }); // before finish
    builder.insert_header(("X-Custom", "value"));
    builder.finish() // terminal call, last
}

Try / catch

// Rust does not catch panics. Ensure .extensions() is called before .finish()/.body().
// If you need extensions after building, use the HttpResponse directly:
let res = HttpResponse::Ok().finish();
// Access on the response, not the builder:
let ext = res.extensions();

Prevention

When it happens

Trigger: Calling `.extensions()` after `.body()`, `.finish()`, `.json()`, or `.streaming()` on the same builder. Also happens if `.extensions()` is called after `.take()`. For example: `let res = builder.finish(); let ext = builder.extensions();` panics.

Common situations: Storing a builder, calling `.finish()` early (perhaps in a helper), then trying to access extensions later. Also occurs with the `From<HttpResponseBuilder>` impl which calls `.finish()` internally.

Related errors


AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09). Data as JSON: /api/errors/f379a80f1bd08111. Report an issue: GitHub.

Appendix: source

Thrown at actix-web/src/response/builder.rs:272

    ///     .finish();
    /// ```
    #[cfg(feature = "cookies")]
    pub fn cookie(&mut self, cookie: cookie::Cookie<'_>) -> &mut Self {
        match cookie.to_string().try_into_value() {
            Ok(hdr_val) => self.append_header((header::SET_COOKIE, hdr_val)),
            Err(err) => {
                self.error = Some(err.into());
                self
            }
        }
    }

    /// Returns a reference to the response-local data/extensions container.
    #[inline]
    pub fn extensions(&self) -> Ref<'_, Extensions> {
        self.res
            .as_ref()
            .expect("cannot reuse response builder")
            .extensions()
    }

    /// Returns a mutable reference to the response-local data/extensions container.
    #[inline]
    pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> {
        self.res
            .as_mut()
            .expect("cannot reuse response builder")
            .extensions_mut()
    }

    /// Set a body and build the `HttpResponse`.
    ///
    /// Unlike [`message_body`](Self::message_body), errors are converted into error
    /// responses immediately.
    ///
    /// `HttpResponseBuilder` can not be used after this call.

View on GitHub (pinned to 4d435abc28)