actix/actix-web · critical

All default headers must be added before cloning.

Error message

All default headers must be added before cloning.

What it means

`DefaultHeaders` stores its header map inside an `Rc<Inner>`. The `add()` method at actix-web/src/middleware/default_headers.rs:65-68 uses `Rc::get_mut`, which returns `None` once the `Rc` has been cloned (refcount > 1). Because `DefaultHeaders` is `Clone` and is cloned when wrapped into the middleware pipeline, calling `add()` after the middleware has been cloned panics with this message.

Source

Thrown at actix-web/src/middleware/default_headers.rs:67

impl DefaultHeaders {
    /// Constructs an empty `DefaultHeaders` middleware.
    #[inline]
    pub fn new() -> DefaultHeaders {
        DefaultHeaders::default()
    }

    /// Adds a header to the default set.
    ///
    /// # Panics
    /// Panics when resolved header name or value is invalid.
    #[allow(clippy::should_implement_trait)]
    pub fn add(mut self, header: impl TryIntoHeaderPair) -> Self {
        // standard header terminology `insert` or `append` for this method would make the behavior
        // of this middleware less obvious since it only adds the headers if they are not present

        match header.try_into_pair() {
            Ok((key, value)) => Rc::get_mut(&mut self.inner)
                .expect("All default headers must be added before cloning.")
                .headers
                .append(key, value),
            Err(err) => panic!("Invalid header: {}", err.into()),
        }

        self
    }

    #[doc(hidden)]
    #[deprecated(
        since = "4.0.0",
        note = "Prefer `.add((key, value))`. Will be removed in v5."
    )]
    pub fn header<K, V>(self, key: K, value: V) -> Self
    where
        HeaderName: TryFrom<K>,
        <HeaderName as TryFrom<K>>::Error: Into<HttpError>,
        HeaderValue: TryFrom<V>,

View on GitHub (pinned to 937960ca67)

Solutions

  1. Chain all `.add(...)` calls in a single builder expression before passing the result to `.wrap(...)`.
  2. Do not clone the `DefaultHeaders` and then mutate it; build a fresh `DefaultHeaders::new()` for each variant.
  3. If you need different default headers per scope, construct separate `DefaultHeaders` instances for each.

Example fix

// before
let common = middleware::DefaultHeaders::new().add(("X-App", "web"));
let v2 = common.clone().add(("X-Version", "2")); // panics

// after
let common = middleware::DefaultHeaders::new().add(("X-App", "web"));
let v2 = middleware::DefaultHeaders::new()
    .add(("X-App", "web"))
    .add(("X-Version", "2"));
Defensive patterns

Strategy: validation

Validate before calling

// Track whether the DefaultHeaders has been cloned/wrapped and refuse to add afterward.
// Simplest rule: build the full header chain in one expression before .wrap(...).
let mw = middleware::DefaultHeaders::new()
    .add(("X-App", "web"))
    .add(("X-Version", "2"));
// only now: .wrap(mw)
// For a second variant, start from DefaultHeaders::new() again.

Prevention

When it happens

Trigger: Calling `.add(...)` on a `DefaultHeaders` value after it has already been `.clone()`d or after `.wrap(...)` consumed it (wrapping clones it into a `Transform`).

Common situations: Storing a `DefaultHeaders` in a shared config, cloning it, then trying to add more headers per-branch; or chaining `.add()` after `.wrap()`.

Related errors


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