netty/netty · error · IllegalArgumentException

can't add to itself.

Error message

can't add to itself.

What it means

Thrown by DefaultHeaders.add(Headers) when the argument is the same instance as 'this' (headers == this). Adding a collection to itself would cause infinite recursion or concurrent modification in the linked-list traversal during addImpl(), so Netty rejects it explicitly. The check is a reference equality (==) check, not a deep equality check.

Source

Thrown at codec-base/src/main/java/io/netty/handler/codec/DefaultHeaders.java:479

    @Override
    public T addFloat(K name, float value) {
        return add(name, fromFloat(name, value));
    }

    @Override
    public T addByte(K name, byte value) {
        return add(name, fromByte(name, value));
    }

    @Override
    public T addShort(K name, short value) {
        return add(name, fromShort(name, value));
    }

    @Override
    public T add(Headers<? extends K, ? extends V, ?> headers) {
        if (headers == this) {
            throw new IllegalArgumentException("can't add to itself.");
        }
        addImpl(headers);
        return thisT();
    }

    protected void addImpl(Headers<? extends K, ? extends V, ?> headers) {
        if (headers instanceof DefaultHeaders) {
            @SuppressWarnings("unchecked")
            final DefaultHeaders<? extends K, ? extends V, T> defaultHeaders =
                    (DefaultHeaders<? extends K, ? extends V, T>) headers;
            HeaderEntry<? extends K, ? extends V> e = defaultHeaders.head.after;
            if (defaultHeaders.hashingStrategy == hashingStrategy &&
                    defaultHeaders.nameValidator == nameValidator) {
                // Fastest copy
                while (e != defaultHeaders.head) {
                    add0(e.hash, index(e.hash), e.key, e.value);
                    e = e.after;
                }

View on GitHub (pinned to 70040aacae)

Solutions

  1. Before calling add(), check reference equality: if (source != target) target.add(source).
  2. If you need to duplicate entries within the same headers, iterate manually and add each entry.
  3. Use a separate headers instance as the merge destination.

Example fix

// before
if (needsMerge) {
    headers.add(headers); // self-reference
}

// after
if (needsMerge && other != headers) {
    headers.add(other);
}
Defensive patterns

Strategy: validation

Validate before calling

// Prevent self-addition
public void mergeHeaders(DefaultHeaders<K, V, T> target, Headers<K, V, ?> source) {
    if (source == target) {
        throw new IllegalArgumentException("Cannot merge headers into itself");
    }
    target.add(source);
}

Type guard

static <K, V> boolean isSelfReference(
        DefaultHeaders<K, V, ?> target, Headers<K, V, ?> source) {
    return source == target;
}

Try / catch

try {
    headers.add(source);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("can't add to itself")) {
        // copy entries individually instead
        for (Map.Entry<K, V> entry : source) {
            headers.add(entry.getKey(), entry.getValue());
        }
    }
}

Prevention

When it happens

Trigger: Calling headers.add(headers) — passing the same DefaultHeaders instance as both source and destination. Occurs in code that copies headers between collections and accidentally passes the source as its own target, or in aggregation/merge logic where the same reference appears on both sides.

Common situations: Header merging/aggregation code where src and dst resolve to the same object due to a logic bug; conditional code paths that sometimes pass the same headers reference; refactoring that changed ownership semantics; a method that receives headers as a parameter and also returns to the same caller.

Related errors


AI-assisted analysis of netty/netty@70040aacae (2026-08-14). Data as JSON: /api/errors/a1856fd1dc0f44b1. Report an issue: GitHub.