jaegertracing/jaeger · error
span_ids is required and must not be empty
Error message
span_ids is required and must not be empty
What it means
The get_span_details handler limits how many spans may be requested per call (h.maxSpanDetailsPerRequest) to keep responses bounded. This error is thrown in buildQuery when len(input.SpanIDs) exceeds that configured limit, before any query is issued.
Source
Thrown at cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/get_span_details.go:131
for spanID := range spanIDSet {
missingIDs = append(missingIDs, spanID)
}
output.Error = fmt.Sprintf("spans not found: %v", missingIDs)
}
return nil, output, nil
}
// buildQuery converts GetSpanDetailsInput to querysvc.GetTraceParams and returns
// the requested span IDs in canonical lowercase hex form for the lookup set.
func (h *getSpanDetailsHandler) buildQuery(input types.GetSpanDetailsInput) (querysvc.GetTraceParams, []string, error) {
// Validate input
if input.TraceID == "" {
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 theView on GitHub (pinned to 806f444784)
Solutions
- Batch the request: split span_ids into chunks at or below the limit and call repeatedly
- Deduplicate span_ids before calling (the handler builds a set anyway)
- Raise maxSpanDetailsPerRequest configuration if responses are legitimately large
- Narrow the span selection to only the spans you actually need
Example fix
// before
out, err := h.Handle(ctx, req, types.GetSpanDetailsInput{TraceID: tid, SpanIDs: allSpanIDs})
// after
for i := 0; i < len(allSpanIDs); i += maxLimit {
end := min(i+maxLimit, len(allSpanIDs))
out, err := h.Handle(ctx, req, types.GetSpanDetailsInput{TraceID: tid, SpanIDs: allSpanIDs[i:end]})
// collect results...
} Defensive patterns
Strategy: validation
Validate before calling
uniq := make(map[string]struct{}, len(input.SpanIDs))
for _, id := range input.SpanIDs { uniq[id] = struct{}{} }
if len(uniq) > maxSpanDetailsPerRequest {
return fmt.Errorf("%d unique span IDs exceeds limit %d; batch the request", len(uniq), maxSpanDetailsPerRequest)
} Try / catch
out, err := handler.Handle(ctx, req, input)
if err != nil && strings.Contains(err.Error(), "exceeds maximum limit") {
// split input.SpanIDs into chunks <= limit and issue multiple calls
} Prevention
- Chunk large span ID lists before calling
- Deduplicate IDs first
- Check the deployment's maxSpanDetailsPerRequest config when planning request sizes
When it happens
Trigger: Passing more span IDs in the span_ids array than the configured maxSpanDetailsPerRequest cap, e.g. requesting hundreds of spans from a wide trace in one call.
Common situations: Trace with thousands of spans (fan-out/batch jobs) and a client requesting all spans at once; a low configured limit after deployment hardening; duplicated IDs inflating the count.
Related errors
- trace_id is required
- start_time must be before end_time
- trace_id is required
- span ID must not be all zero
- service_name is required
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/ded9a88e7db38db1.
Report an issue: GitHub.