jaegertracing/jaeger · error

invalid trace_id: %w

Error message

invalid trace_id: %w

What it means

buildQuery parses input.TraceID with parseTraceID, which requires a 32-character hex string representing a 16-byte pcommon.TraceID. Any parse failure (wrong length or non-hex characters) is wrapped as "invalid trace_id" so the caller knows the identifier itself is malformed.

Source

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

		return querysvc.GetTraceParams{}, nil, errors.New("trace_id is required")
	}

	if len(input.SpanIDs) == 0 {
		return querysvc.GetTraceParams{}, nil, errors.New("span_ids is required and must not be empty")
	}

	// Validate span count against configured limit
	if len(input.SpanIDs) > h.maxSpanDetailsPerRequest {
		return querysvc.GetTraceParams{}, nil, fmt.Errorf(
			"span_ids exceeds maximum limit: requested %d, max allowed %d",
			len(input.SpanIDs),
			h.maxSpanDetailsPerRequest,
		)
	}

	traceID, err := parseTraceID(input.TraceID)
	if err != nil {
		return querysvc.GetTraceParams{}, nil, fmt.Errorf("invalid trace_id: %w", err)
	}

	// Validate every span_id up front so a malformed value fails fast instead of
	// triggering a backend query that can never match a real span ID. Collect the
	// canonical lowercase hex form so the lookup set agrees with the ID the trace
	// iterator emits regardless of the request's casing.
	canonicalSpanIDs := make([]string, 0, len(input.SpanIDs))
	for _, spanIDStr := range input.SpanIDs {
		spanID, err := parseSpanID(spanIDStr)
		if err != nil {
			return querysvc.GetTraceParams{}, nil, fmt.Errorf("invalid span_id %q: %w", spanIDStr, err)
		}
		canonicalSpanIDs = append(canonicalSpanIDs, spanID.String())
	}

	return querysvc.GetTraceParams{
		TraceIDs: []tracestore.GetTraceParams{
			{TraceID: traceID},

View on GitHub (pinned to 806f444784)

Solutions

  1. Provide the full 32-character lowercase hex trace ID, e.g. "4bf92f3577b34da6a3ce929d0e0e4736".
  2. Left-pad 16-hex-char legacy IDs with zeros to 32 characters.
  3. Strip any "0x" prefix, dashes, or whitespace before calling the tool.
  4. Confirm you are not passing a span ID where trace_id is expected.

Example fix

// before
TraceID: "4bf92f3577b34da6" // 16 chars
// after
TraceID: "00000000000000004bf92f3577b34da6" // padded to 32 hex chars
Defensive patterns

Strategy: validation

Validate before calling

func validTraceID(s string) bool {
	if len(s) != 32 { return false }
	_, err := hex.DecodeString(s)
	return err == nil
}

Type guard

func normalizeTraceID(s string) (string, bool) {
	s = strings.TrimPrefix(strings.TrimSpace(s), "0x")
	if len(s) < 16 || len(s) > 32 { return "", false }
	if _, err := hex.DecodeString(s); err != nil { return "", false }
	return fmt.Sprintf("%032s", s), true
}

Try / catch

out, _, err := handler.Handle(ctx, req, input)
if err != nil && strings.HasPrefix(err.Error(), "invalid trace_id") {
	id, ok := normalizeTraceID(input.TraceID)
	if !ok { return err }
	input.TraceID = id
	out, _, err = handler.Handle(ctx, req, input)
}

Prevention

When it happens

Trigger: Passing a 16-character (8-byte) legacy-style ID without padding, an ID containing '0x' prefix or dashes, a decimal-formatted ID, or an empty string, to the get_span_details tool.

Common situations: Mixing IDs from systems using 64-bit span/trace IDs (W3C 8-byte traces) with Jaeger's 128-bit format; copying IDs from logs with formatting applied; a client passing a span ID into the trace_id field.

Related errors


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