{"record":{"id":"a5da5613f4307bcd","repo":"jaegertracing/jaeger","slug":"w-w-a5da56","errorCode":null,"errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/v1/cassandra/spanstore/reader.go","lineNumber":227,"sourceCode":"\treturn spans, nil\n}\n\n// GetTrace takes a traceID and returns the spans associated with that traceID\nfunc (s *SpanReader) GetTrace(ctx context.Context, traceID dbmodel.TraceID) ([]dbmodel.Span, error) {\n\treturn s.readTrace(ctx, traceID)\n}\n\nfunc validateQuery(p *tracestore.TraceQueryParams) error {\n\tif p == nil {\n\t\treturn ErrMalformedRequestObject\n\t}\n\t// Every index is keyed by service name, so a query without one has no partition to\n\t// read: queryByService would run with an empty partition key and return zero rows,\n\t// which is indistinguishable from \"no matching traces\". Refusing it says what is\n\t// actually true (RFC 0013 §3.3); the query service normally rejects such a query\n\t// first, from the capability this reader declares.\n\tif p.ServiceName == \"\" {\n\t\treturn fmt.Errorf(\"%w: %w\", ErrServiceNameNotSet, errors.ErrUnsupported)\n\t}\n\tif p.StartTimeMin.IsZero() || p.StartTimeMax.IsZero() {\n\t\treturn ErrStartAndEndTimeNotSet\n\t}\n\tif !p.StartTimeMin.IsZero() && !p.StartTimeMax.IsZero() && p.StartTimeMax.Before(p.StartTimeMin) {\n\t\treturn ErrStartTimeMinGreaterThanMax\n\t}\n\tif p.DurationMin != 0 && p.DurationMax != 0 && p.DurationMin > p.DurationMax {\n\t\treturn ErrDurationMinGreaterThanMax\n\t}\n\tif (p.DurationMin != 0 || p.DurationMax != 0) && p.Attributes.Len() > 0 {\n\t\treturn ErrDurationAndTagQueryNotSupported\n\t}\n\treturn nil\n}\n\n// FindTraces retrieves traces that match the traceQuery\nfunc (s *SpanReader) FindTraces(ctx context.Context, traceQuery *tracestore.TraceQueryParams) iter.Seq2[dbmodel.Trace, error] {","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v1/cassandra/spanstore/reader.go#L209-L245","documentation":"validateQuery rejects trace queries that lack a service name by wrapping ErrServiceNameNotSet together with errors.ErrUnsupported (double %w, Go 1.20 multi-error wrapping). The Cassandra index is partitioned by service, so a service-less query cannot be executed — it would scan an empty partition and be indistinguishable from 'no traces found'.","triggerScenarios":"Calling FindTraceIDs (or FindTraces) with tracestore.Query where ServiceName == \"\"; also asserted in TestTraceQueryParameterValidation. The query service normally blocks this earlier via declared capabilities, but direct storage clients hit it here.","commonSituations":"Custom tooling or tests invoking SpanReader.FindTraceIDs directly without filling Query.ServiceName; UI/API regressions that drop the service parameter; scripts built against other backends that allow global queries.","solutions":["Set Query.ServiceName to a non-empty value before calling FindTraceIDs/FindTraces.","Check errors.Is(err, ErrServiceNameNotSet) and errors.Is(err, errors.ErrUnsupported) to detect this case programmatically and prompt the user for a service.","If you need service-less searches, use a backend/index that supports them (e.g. Elasticsearch/OpenSearch storage) rather than Cassandra.","Update calling code to rely on the query service's capability check so the query is validated before reaching storage."],"exampleFix":"// before\nspans, err := reader.FindTraceIDs(ctx, tracestore.Query{StartTimeMin: t1, StartTimeMax: t2})\n// after\nspans, err := reader.FindTraceIDs(ctx, tracestore.Query{ServiceName: \"frontend\", StartTimeMin: t1, StartTimeMax: t2})","handlingStrategy":"validation","validationCode":"if q.ServiceName == \"\" {\n    return errors.New(\"Query.ServiceName is required for Cassandra trace queries\")\n}\nif q.StartTimeMin.IsZero() || q.StartTimeMax.IsZero() {\n    return errors.New(\"Query start/end times are required\")\n}\nif q.StartTimeMax.Before(q.StartTimeMin) {\n    return errors.New(\"StartTimeMax must not be before StartTimeMin\")\n}","typeGuard":"func isValidQuery(q tracestore.Query) bool {\n    return q.ServiceName != \"\" && !q.StartTimeMin.IsZero() && !q.StartTimeMax.IsZero() && !q.StartTimeMax.Before(q.StartTimeMin)\n}","tryCatchPattern":"ids, err := reader.FindTraceIDs(ctx, q)\nif err != nil {\n    if errors.Is(err, spanstore.ErrServiceNameNotSet) || errors.Is(err, errors2.ErrUnsupported) {\n        // surface 'service name required' to the caller\n    }\n    return err\n}","preventionTips":["Always populate ServiceName before querying Cassandra storage.","Use errors.Is against ErrServiceNameNotSet/ErrUnsupported for precise handling.","Rely on the query service's capability checks to filter unsupported queries early.","Validate time range order alongside service name in client code."],"tags":["validation","query","cassandra","unsupported"],"backgroundTag":"missing-required-argument","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}