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
- Chain all `.add(...)` calls in a single builder expression before passing the result to `.wrap(...)`.
- Do not clone the `DefaultHeaders` and then mutate it; build a fresh `DefaultHeaders::new()` for each variant.
- 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
- Never call .add() on a cloned or already-wrapped DefaultHeaders.
- Construct a fresh DefaultHeaders per distinct header set.
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
- Invalid header name
- Invalid header value
- cannot reuse response builder
- actix-http client only supports versions http/1.1 & http/2
- Unsupported HTTP version: {:?}.
AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06).
Data as JSON: /data/errors/657c56aeb60e1c84.json.
Report an issue: GitHub.