CodeSampleX

Ejemplo

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

Muestra verificada para golang go.opentelemetry.io/otel v1.45.0: SpanFromContext never returns nil on uninstrumented contexts, ContextWithRemoteSpanContext…

sha256:6ce5d5513105a8ec6185c85a379474fc0c264a342723cf6da8ea4fa0c4591c49

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
3
Claves de firma que lo compilaron
2
Entorno declarado go linux x64 go go gomod

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
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

Caso

HOW
Objetivo
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
Paquetes
Símbolos
  • 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
Entorno
go
Creado
2026-08-16T07:51:55Z

Contrato

  1. assert SpanFromContext returns non-nil noopSpan with invalid span context on nil or empty context
  2. assert ContextWithRemoteSpanContext produces non-recording span that silently drops SetAttributes and SetStatus
  3. assert Tracer Start on remote context inherits trace ID and parent span ID for local recording child
  4. assert WithNewRoot severs remote parent context generating fresh trace ID with zero parent ID
  5. assert TraceIDFromHex and SpanIDFromHex reject uppercase hex strings and all-zeros per W3C specification
  6. assert TraceState Insert is immutable, prepends keys to index 0, and rejects uppercase key names

Archivos

  • NOTES.md
  • csx.json
  • go.mod
  • go.sum
  • trace_context_test.go

Descargar el artefacto de código fuente (tar.gz)

Código fuente

NOTES.md
# 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.
csx.json
{"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"}
go.mod
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
)
go.sum
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=
trace_context_test.go
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())
		}
	})
}

Seeder de origen

csx-seed