Description
Tracer Version(s)
1.72.2
Go Version(s)
go version go1.24.0 darwin/arm64
Bug Report
Summary
When propagating baggage, dd-trace-go's default behavior diverges from the Datadog documentation, using multiple ot-baggage-*
headers instead of a single baggage
header per the W3C spec the docs claim to follow.
Details
The documentation here says:
Baggage
Currently available in Python, Ruby, PHP, Java, Node.js, C++, Go and .NET. For other languages, please reach out to SupportBy default, Baggage is automatically propagated through a distributed request using OpenTelemetry’s W3C-compatible headers.
The linked to W3C spec specifies baggage propagation via the baggage
HTTP header. However, dd-trace-go
actually appears to use the older ot-baggage-
header prefix style, and there doesn't appear to be a way to convince it to do otherwise.
Why's it matter?
In addition to cross-tracing-system interoperability issues, the ot-baggage
-style headers have a drawback when used in combination with dd-trace-py
on the receiving end: because of how Python's WSGI normalizes -
s in header names to _
s, dd-trace-py
cannot tell the difference between some-key
and some_key
. This can lead to confusion when the callee is expecting the former but gets the latter.
Using the newer single baggage
would address this, because baggage item names appear in the HTTP header value, not the header name.
Contrast to dd-trace-py
dd-trace-py
follows the docs and uses baggage
by default.
Reproduction Code
main.go:
package main
import (
"context"
"log"
"net/http"
httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
func main() {
tracer.Start(
tracer.WithServiceName("ddtest"),
tracer.WithEnv("local"),
)
defer tracer.Stop()
ctx := context.Background()
span, ctx := tracer.StartSpanFromContext(ctx, "main")
defer span.Finish()
span.SetBaggageItem("test-baggage-key", "test-baggage-value")
client := httptrace.WrapClient(&http.Client{})
req, err := http.NewRequestWithContext(ctx, "GET", "https://datadoghq.com", nil)
if err != nil {
log.Fatalf("Error creating request: %v", err)
}
_, err = client.Do(req)
if err != nil {
log.Fatalf("Error making request: %v", err)
}
tracer.Flush()
}
Run like this to demonstrate which header is being used:
❯ GODEBUG=http2debug=1 go run . 2>&1 | grep -i baggage
2025/04/25 20:46:39 http2: Transport encoding header "ot-baggage-test-baggage-key" = "test-baggage-value"
2025/04/25 20:46:39 http2: Transport encoding header "ot-baggage-test-baggage-key" = "test-baggage-value"
Note that alternate values for DD_TRACE_PROPAGATION_STYLE
as described here do not result in baggage
being used either:
DD_TRACE_PROPAGATION_STYLE=datadog
->ot-baggage-*
headersDD_TRACE_PROPAGATION_STYLE=tracecontext
-> no baggage headersDD_TRACE_PROPAGATION_SYTLE=b3multi
-> no baggage headersDD_TRACE_PROPAGATION_STYLE=none
-> no baggage headers
Error Logs
No response
Go Env Output
No response