Describe the bug
Removing the same key twice via TraceStateBuilder.remove(key) corrupts the builder: build() then leaks a null-valued entry, silently drops live entries, or throws ArrayIndexOutOfBoundsException.
Steps to reproduce
TraceState.builder()
.put("a", "1").put("b", "2").put("c", "3")
.remove("a").remove("a")
.build(); // ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
With a=1 alone, build() returns size=1, asMap={a=null}. With a=1,b=2, it returns an empty TraceState — the live b=2 is gone.
What did you expect to see?
Removing an already-removed key is a no-op, per the remove(String) Javadoc ("if it is present") and the TraceStateTest.removeNotPresent() contract. A caller cannot distinguish a key that was never present from one it already removed, yet only the latter breaks.
What did you see instead?
ArrayBasedTraceStateBuilder.remove() (api/all/src/main/java/io/opentelemetry/api/trace/ArrayBasedTraceStateBuilder.java line 95-96) decrements numEntries without checking whether the entry's value is already null, so the counter drifts below the real count. The sibling put() (line 74-78) performs the symmetric check. build() trusts numEntries for its early return, fast path, and array size.
What version and what artifacts are you using?
Artifacts: opentelemetry-api
Version: main @ 63ebd94
How did you reference these artifacts? N/A (source checkout)
Environment
Compiler: Temurin 21
OS: N/A
Additional context
Reachable through the builder-reuse pattern reuseBuilder() guarantees.
Describe the bug
Removing the same key twice via
TraceStateBuilder.remove(key)corrupts the builder:build()then leaks a null-valued entry, silently drops live entries, or throwsArrayIndexOutOfBoundsException.Steps to reproduce
With
a=1alone,build()returnssize=1, asMap={a=null}. Witha=1,b=2, it returns an empty TraceState — the liveb=2is gone.What did you expect to see?
Removing an already-removed key is a no-op, per the
remove(String)Javadoc ("if it is present") and theTraceStateTest.removeNotPresent()contract. A caller cannot distinguish a key that was never present from one it already removed, yet only the latter breaks.What did you see instead?
ArrayBasedTraceStateBuilder.remove()(api/all/src/main/java/io/opentelemetry/api/trace/ArrayBasedTraceStateBuilder.javaline 95-96) decrementsnumEntrieswithout checking whether the entry's value is already null, so the counter drifts below the real count. The siblingput()(line 74-78) performs the symmetric check.build()trustsnumEntriesfor its early return, fast path, and array size.What version and what artifacts are you using?
Artifacts: opentelemetry-api
Version: main @ 63ebd94
How did you reference these artifacts? N/A (source checkout)
Environment
Compiler: Temurin 21
OS: N/A
Additional context
Reachable through the builder-reuse pattern
reuseBuilder()guarantees.