jaegertracing/jaeger · error

span ID must not be all zero

Error message

span ID must not be all zero

What it means

parseSpanID decodes a hex span ID into pcommon.SpanID and rejects the all-zero value, because SpanID.String() renders it as "" and it would silently never match any real span during the filtered lookup. This guard turns a silent no-match into an explicit input error.

Source

Thrown at cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/get_span_details.go:305

// parseSpanID parses a span ID string into a pcommon.SpanID.
func parseSpanID(spanIDStr string) (pcommon.SpanID, error) {
	// Parse hex string - SpanID is 8 bytes (16 hex characters)
	if len(spanIDStr) != 16 {
		return pcommon.SpanID{}, fmt.Errorf("span ID must be 16 hex characters, got %d", len(spanIDStr))
	}

	var spanID pcommon.SpanID
	bytes, err := hex.DecodeString(spanIDStr)
	if err != nil {
		return pcommon.SpanID{}, fmt.Errorf("invalid hex string: %w", err)
	}

	copy(spanID[:], bytes)
	// The all-zero span ID can never identify a real span: SpanID.String()
	// returns "" for it, so it would silently never match in the lookup.
	if spanID.IsEmpty() {
		return pcommon.SpanID{}, errors.New("span ID must not be all zero")
	}
	return spanID, nil
}

View on GitHub (pinned to 806f444784)

Solutions

  1. Replace the zero span ID with the real span ID from the trace's span metadata
  2. Filter out all-zero span IDs client-side before calling the tool
  3. Fix the upstream producer that emitted a zero SpanID (usually an unset field in an exported trace)

Example fix

// before
ids := []string{span.ID().String()} // may be "0000000000000000" if span unset
// after
var filtered []string
for _, id := range candidateIDs {
    if id != "0000000000000000" && strings.Trim(id, "0") != "" {
        filtered = append(filtered, id)
    }
}
out, err := h.Handle(ctx, req, types.GetSpanDetailsInput{TraceID: tid, SpanIDs: filtered})
Defensive patterns

Strategy: validation

Validate before calling

func isZeroSpanID(id string) bool {
    b, err := hex.DecodeString(id)
    return err != nil || !bytes.Contains(b, []byte{1}) // all zeros or undecodable
}
ids := slices.DeleteFunc(rawIDs, isZeroSpanID)

Try / catch

out, err := handler.Handle(ctx, req, input)
if err != nil && strings.Contains(err.Error(), "span ID must not be all zero") {
    return fmt.Errorf("input contained an unset/zero span ID: %v", input.SpanIDs)
}

Prevention

When it happens

Trigger: Passing span_ids entries of "0000000000000000" (or all-zero after hex decoding) to get_span_details, so parseSpanID returns this error during buildQuery.

Common situations: Placeholder/nil span IDs from unmarshaled protobuf or zero-valued structs; a default-initialized SpanID stringified and fed back in; copy-paste of an all-zero sentinel from logs.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/06fe750ce0e964dd. Report an issue: GitHub.