Beispiel
go.opentelemetry.io/otel v1.45.0: SpanFromContext never returns nil on uninstrumented contexts, ContextWithRemoteSpanContext produces non-recording spans that silently drop in-place mutations, TraceIDFromHex rejects uppercase hex, and TraceState mutations are strictly immutable
Verifiziertes Beispiel für golang go.opentelemetry.io/otel v1.45.0: SpanFromContext never returns nil on uninstrumented contexts…
sha256:6ce5d5513105a8ec6185c85a379474fc0c264a342723cf6da8ea4fa0c4591c49
Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen.
Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen.
MIT-0
Ausführungsbelege
Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.
- Beleggrundlage
- Signierter Vertrag bestanden
- Verifizierungsbelege
- 3
- Signaturschlüssel, die es gebaut haben
- 2
Deklarierte Umgebung
go linux x64 go go gomod
Umgebungen der Verifizierungsläufe
| Umgebung | Contract | Stufen | Lauf |
|---|---|---|---|
| go 1.26 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · golang@1 |
2026-08-16 |
| go 1.26 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · golang@1 |
2026-08-18 |
| go 1.26 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | FAIL | compile:SKIPPED · contract:FAIL · load:SKIPPED · resolve:PASS CONTAINER_RUN · golang@1golang:1.26-alpine@sha256:28d89ee9cc0f… |
2026-09-07 |
Fall
HOW- Ziel
- SpanFromContext never returns nil on uninstrumented contexts, ContextWithRemoteSpanContext produces non-recording spans that silently drop in-place mutations, TraceIDFromHex rejects uppercase hex, and TraceState mutations are strictly immutable
- Pakete
- Symbole
-
- go.opentelemetry.io/otel/trace.SpanFromContext
- go.opentelemetry.io/otel/trace.ContextWithRemoteSpanContext
- go.opentelemetry.io/otel/trace.WithNewRoot
- go.opentelemetry.io/otel/trace.TraceIDFromHex
- go.opentelemetry.io/otel/trace.SpanIDFromHex
- go.opentelemetry.io/otel/trace.TraceState
- Umgebung
- go
- Erstellt
- 2026-08-16T07:51:55Z
Contract
- assert SpanFromContext returns non-nil noopSpan with invalid span context on nil or empty context
- assert ContextWithRemoteSpanContext produces non-recording span that silently drops SetAttributes and SetStatus
- assert Tracer Start on remote context inherits trace ID and parent span ID for local recording child
- assert WithNewRoot severs remote parent context generating fresh trace ID with zero parent ID
- assert TraceIDFromHex and SpanIDFromHex reject uppercase hex strings and all-zeros per W3C specification
- assert TraceState Insert is immutable, prepends keys to index 0, and rejects uppercase key names
Dateien
- NOTES.md
- csx.json
- go.mod
- go.sum
- trace_context_test.go
Quelltext
# Notes
- **search_known_solution result**: The network returned sample `sha256:1bf17bde93ae26bd833bf9e707662cf033b05798d2ab0c1289d2d691da1b1942` ("RecordError records an exception event leaving span status as Unset, while SetStatus Ok irrevocably locks the span status against subsequent Error updates"). We selected a different set of behavioral traps in `go.opentelemetry.io/otel/trace`: `SpanFromContext` never returns `nil` (returning a non-nil `noopSpan`), `ContextWithRemoteSpanContext` yields a `nonRecordingSpan` that silently drops in-place `SetAttributes`/`SetStatus` calls without error, `WithNewRoot()` severs inherited remote trace IDs, `TraceIDFromHex`/`SpanIDFromHex` strictly reject uppercase hex per W3C specification unlike standard Go hex decoders, and `TraceState.Insert` is value-immutable and fails on uppercase keys.
- **What a model would write instead**: A model writes `if span := trace.SpanFromContext(ctx); span != nil` to detect tracing, attempts to mutate remote spans directly with `span.SetAttributes(...)` after remote context extraction, parses uppercase hex strings via `TraceIDFromHex` assuming standard hex tolerance, and ignores the return value of `ts.Insert(...)` assuming in-place mutation.
- **How the wrong version fails**: Fails silently with a green build: the application compiles and runs without error, but presence checks (`span != nil`) always evaluate true, attributes attached to extracted remote contexts are dropped silently without recording, traces become disconnected or orphan when `WithNewRoot` is misused, and TraceState modifications are lost.
{"case":{"caseId":"case:sha256:941d72b77be63276d05f78fd013510e9a0be4841138a4fcdf10768585866feb5","contract":["assert SpanFromContext returns non-nil noopSpan with invalid span context on nil or empty context","assert ContextWithRemoteSpanContext produces non-recording span that silently drops SetAttributes and SetStatus","assert Tracer Start on remote context inherits trace ID and parent span ID for local recording child","assert WithNewRoot severs remote parent context generating fresh trace ID with zero parent ID","assert TraceIDFromHex and SpanIDFromHex reject uppercase hex strings and all-zeros per W3C specification","assert TraceState Insert is immutable, prepends keys to index 0, and rejects uppercase key names"],"goal":"SpanFromContext never returns nil on uninstrumented contexts, ContextWithRemoteSpanContext produces non-recording spans that silently drop in-place mutations, TraceIDFromHex rejects uppercase hex, and TraceState mutations are strictly immutable","kind":"HOW","packages":["pkg:golang/go.opentelemetry.io/otel@v1.45.0","pkg:golang/go.opentelemetry.io/otel/trace@v1.45.0","pkg:golang/go.opentelemetry.io/otel/sdk@v1.45.0"],"schemaVersion":1,"symbols":["go.opentelemetry.io/otel/trace.SpanFromContext","go.opentelemetry.io/otel/trace.ContextWithRemoteSpanContext","go.opentelemetry.io/otel/trace.WithNewRoot","go.opentelemetry.io/otel/trace.TraceIDFromHex","go.opentelemetry.io/otel/trace.SpanIDFromHex","go.opentelemetry.io/otel/trace.TraceState"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","ecosystem":"golang","executionContext":"go","language":"go","os":"linux","packageManager":"gomod","runtime":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/go.opentelemetry.io/otel@v1.45.0","pkg:golang/go.opentelemetry.io/otel/trace@v1.45.0","pkg:golang/go.opentelemetry.io/otel/sdk@v1.45.0"],"schemaVersion":1,"symbols":["go.opentelemetry.io/otel/trace.SpanFromContext","go.opentelemetry.io/otel/trace.ContextWithRemoteSpanContext","go.opentelemetry.io/otel/trace.WithNewRoot","go.opentelemetry.io/otel/trace.TraceIDFromHex","go.opentelemetry.io/otel/trace.SpanIDFromHex","go.opentelemetry.io/otel/trace.TraceState"],"verifierAdapter":"golang@1"}
module sample
go 1.26.5
require (
go.opentelemetry.io/otel v1.45.0
go.opentelemetry.io/otel/sdk v1.45.0
go.opentelemetry.io/otel/trace v1.45.0
)
require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
golang.org/x/sys v0.47.0 // indirect
)
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package sample_test
import (
"context"
"testing"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.opentelemetry.io/otel/trace"
)
func TestContextAndSpanContextSemantics(t *testing.T) {
ctx := context.Background()
t.Run("SpanFromContext never returns nil even on empty or nil context", func(t *testing.T) {
// Trap: A naive developer checks `if span != nil` to test if a context is traced.
// SpanFromContext returns a non-nil noopSpanInstance with IsValid() == false.
spanNilCtx := trace.SpanFromContext(nil)
if spanNilCtx == nil {
t.Fatal("expected non-nil Span from nil context, got nil")
}
if spanNilCtx.SpanContext().IsValid() {
t.Errorf("expected invalid SpanContext from nil context")
}
if spanNilCtx.IsRecording() {
t.Errorf("expected non-recording span from nil context")
}
spanEmptyCtx := trace.SpanFromContext(ctx)
if spanEmptyCtx == nil {
t.Fatal("expected non-nil Span from empty context, got nil")
}
if spanEmptyCtx.SpanContext().IsValid() {
t.Errorf("expected invalid SpanContext from empty context")
}
if spanEmptyCtx.IsRecording() {
t.Errorf("expected non-recording span from empty context")
}
// Calling methods on noopSpan is silently ignored (does not panic)
spanEmptyCtx.SetAttributes(attribute.String("key", "val"))
spanEmptyCtx.SetStatus(codes.Error, "failed")
spanEmptyCtx.End()
})
t.Run("ContextWithRemoteSpanContext wraps remote context in non-recording span", func(t *testing.T) {
traceID, err := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
if err != nil {
t.Fatalf("unexpected error parsing traceID: %v", err)
}
spanID, err := trace.SpanIDFromHex("00f067aa0ba902b7")
if err != nil {
t.Fatalf("unexpected error parsing spanID: %v", err)
}
remoteSC := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
})
// Injected as remote span context
remoteCtx := trace.ContextWithRemoteSpanContext(ctx, remoteSC)
extractedSpan := trace.SpanFromContext(remoteCtx)
// The span extracted is a nonRecordingSpan: valid and remote, but NOT recording!
if !extractedSpan.SpanContext().IsValid() {
t.Errorf("expected valid SpanContext from remote context")
}
if !extractedSpan.SpanContext().IsRemote() {
t.Errorf("expected IsRemote() == true on remote span context")
}
if extractedSpan.IsRecording() {
t.Errorf("expected IsRecording() == false on remote non-recording span")
}
// Mutating the extracted non-recording span is silently dropped
extractedSpan.SetAttributes(attribute.String("dropped.attr", "true"))
extractedSpan.SetStatus(codes.Error, "dropped error")
extractedSpan.End()
// Starting a child span with TracerProvider propagates the remote parent
exporter := tracetest.NewInMemoryExporter()
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter))
tracer := tp.Tracer("server-tracer")
childCtx, childSpan := tracer.Start(remoteCtx, "handle-request")
childSpan.SetAttributes(attribute.String("handler.status", "ok"))
childSpan.End()
spans := exporter.GetSpans()
if len(spans) != 1 {
t.Fatalf("expected 1 exported child span, got %d", len(spans))
}
s := spans[0]
// Child inherits TraceID and Parent SpanID
if s.SpanContext.TraceID() != traceID {
t.Errorf("expected child traceID %s, got %s", traceID, s.SpanContext.TraceID())
}
if s.Parent.SpanID() != spanID {
t.Errorf("expected child parent spanID %s, got %s", spanID, s.Parent.SpanID())
}
if s.SpanContext.IsRemote() {
t.Errorf("expected child span IsRemote() == false")
}
_ = childCtx
})
t.Run("WithNewRoot severs parent trace context and starts fresh trace", func(t *testing.T) {
traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7")
remoteSC := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
})
remoteCtx := trace.ContextWithRemoteSpanContext(ctx, remoteSC)
exporter := tracetest.NewInMemoryExporter()
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter))
tracer := tp.Tracer("boundary-tracer")
// Starting span with WithNewRoot
_, rootSpan := tracer.Start(remoteCtx, "isolated-root", trace.WithNewRoot())
rootSpan.End()
spans := exporter.GetSpans()
if len(spans) != 1 {
t.Fatalf("expected 1 exported span, got %d", len(spans))
}
s := spans[0]
// Trap: WithNewRoot ignores parent in context: assigns fresh TraceID and no parent
if s.SpanContext.TraceID() == traceID {
t.Errorf("expected new TraceID different from parent %s", traceID)
}
if s.Parent.IsValid() {
t.Errorf("expected invalid parent for new root, got %v", s.Parent)
}
})
t.Run("TraceIDFromHex rejects uppercase hex and all-zeros per W3C specification", func(t *testing.T) {
// Valid lowercase hex succeeds
validID, err := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
if err != nil || !validID.IsValid() {
t.Fatalf("expected valid TraceID, got err=%v", err)
}
// Trap: Standard hex decoders allow uppercase, but W3C trace context & OTel reject uppercase
_, errUpper := trace.TraceIDFromHex("4BF92F3577B34DA6A3CE929D0E0E4736")
if errUpper == nil {
t.Errorf("expected error for uppercase TraceID hex, got nil")
}
// Trap: All-zero hex is rejected with errNilTraceID
_, errZeros := trace.TraceIDFromHex("00000000000000000000000000000000")
if errZeros == nil {
t.Errorf("expected error for all-zero TraceID hex, got nil")
}
// SpanIDFromHex also rejects uppercase and all-zeros
_, errSpanUpper := trace.SpanIDFromHex("00F067AA0BA902B7")
if errSpanUpper == nil {
t.Errorf("expected error for uppercase SpanID hex, got nil")
}
_, errSpanZeros := trace.SpanIDFromHex("0000000000000000")
if errSpanZeros == nil {
t.Errorf("expected error for all-zero SpanID hex, got nil")
}
})
t.Run("TraceState is immutable and enforces lowercase W3C keys and capacity", func(t *testing.T) {
ts, err := trace.ParseTraceState("vendor1=alpha,vendor2=beta")
if err != nil {
t.Fatalf("unexpected error parsing tracestate: %v", err)
}
if ts.Len() != 2 {
t.Fatalf("expected 2 members, got %d", ts.Len())
}
// Trap 1: Immutability - calling Insert returns a new copy and does NOT mutate receiver
tsNew, err := ts.Insert("vendor3", "gamma")
if err != nil {
t.Fatalf("unexpected error on valid Insert: %v", err)
}
if ts.Len() != 2 {
t.Errorf("original TraceState was mutated: expected len 2, got %d", ts.Len())
}
if ts.Get("vendor3") != "" {
t.Errorf("original TraceState should not contain vendor3")
}
if tsNew.Len() != 3 {
t.Errorf("expected new TraceState len 3, got %d", tsNew.Len())
}
// Trap 2: Insert prepends new member to the front (index 0)
if tsNew.String() != "vendor3=gamma,vendor1=alpha,vendor2=beta" {
t.Errorf("expected vendor3 at front, got %q", tsNew.String())
}
// Trap 3: Updating an existing key moves it to the front
tsUpdated, err := tsNew.Insert("vendor2", "updated_beta")
if err != nil {
t.Fatalf("unexpected error updating existing key: %v", err)
}
if tsUpdated.String() != "vendor2=updated_beta,vendor3=gamma,vendor1=alpha" {
t.Errorf("expected updated vendor2 at front, got %q", tsUpdated.String())
}
// Trap 4: Uppercase key fails W3C validation and returns errInvalidKey
_, errUpperKey := ts.Insert("VendorKey", "val")
if errUpperKey == nil {
t.Errorf("expected error when inserting uppercase key into TraceState")
}
// Trap 5: Delete is also immutable
tsDeleted := tsNew.Delete("vendor1")
if tsNew.Len() != 3 {
t.Errorf("expected original tsNew to remain len 3, got %d", tsNew.Len())
}
if tsDeleted.Len() != 2 || tsDeleted.Get("vendor1") != "" {
t.Errorf("expected tsDeleted len 2 without vendor1, got %q", tsDeleted.String())
}
})
}