actix/actix-web · critical

cannot reuse response builder

Error message

cannot reuse response builder

What it means

`HttpResponseBuilder` holds the in-progress response in `res: Option<...>`. Consuming methods (`body`, `message_body`, `finish`) call `.take()`, leaving `res = None`. `extensions()` at actix-web/src/response/builder.rs:269-273 reads `self.res.as_ref().expect("cannot reuse response builder")`, so calling `extensions()` after the builder was finished panics.

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 937960ca67)

Solutions

  1. Treat `finish()`/`body()`/`message_body()` as the terminal call — never use the builder afterward.
  2. Read `extensions()`/`extensions_mut()` before finishing the response.
  3. If you need extension data after building, capture it from the returned `HttpResponse` via `res.extensions()` instead of the builder.

Example fix

// before
let mut b = HttpResponse::Ok();
b.insert_header(("X", "1"));
let resp = b.finish();
let ext = b.extensions(); // panics

// after
let mut b = HttpResponse::Ok();
b.insert_header(("X", "1"));
let ext = b.extensions();      // read before finish
let resp = b.finish();
// or use resp.extensions() afterwards
Defensive patterns

Strategy: validation

Validate before calling

// Treat the builder as single-use. If you must track state, wrap usage so that
// extensions()/extensions_mut() are only called before a terminal method.
// Pseudocode guard for your own helpers:
struct SafeBuilder { b: Option<HttpResponseBuilder> }
impl SafeBuilder {
    fn extensions(&self) -> bool { self.b.is_some() }
    fn finish(&mut self) -> HttpResponse {
        self.b.take().unwrap().finish()
    }
}

Prevention

When it happens

Trigger: Calling `.finish()` (or `.body(...)`) on a builder, then calling `.extensions()` on the same builder instance.

Common situations: Storing a builder in a variable, finishing it, and accidentally touching it again; or shared/multiple-finish code paths.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/f379a80f1bd08111.json. Report an issue: GitHub.