{"record":{"id":"67e88c6f2b9319d3","repo":"jaegertracing/jaeger","slug":"start-time-max-must-be-after-start-time-min","errorCode":null,"errorMessage":"start_time_max must be after start_time_min","messagePattern":"start_time_max must be after start_time_min","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/search_traces.go","lineNumber":142,"sourceCode":"\t}\n\n\tminStartTime, err := parseTimeParam(startTimeMinInput)\n\tif err != nil {\n\t\treturn querysvc.TraceQueryParams{}, fmt.Errorf(\"invalid start_time_min: %w\", err)\n\t}\n\n\tvar maxStartTime time.Time\n\tif input.StartTimeMax != \"\" {\n\t\tmaxStartTime, err = parseTimeParam(input.StartTimeMax)\n\t\tif err != nil {\n\t\t\treturn querysvc.TraceQueryParams{}, fmt.Errorf(\"invalid start_time_max: %w\", err)\n\t\t}\n\t} else {\n\t\tmaxStartTime = time.Now()\n\t}\n\n\tif !maxStartTime.IsZero() && maxStartTime.Before(minStartTime) {\n\t\treturn querysvc.TraceQueryParams{}, errors.New(\"start_time_max must be after start_time_min\")\n\t}\n\n\tvar durationMin, durationMax time.Duration\n\tif input.DurationMin != \"\" {\n\t\tdurationMin, err = time.ParseDuration(input.DurationMin)\n\t\tif err != nil {\n\t\t\treturn querysvc.TraceQueryParams{}, fmt.Errorf(\"invalid duration_min: %w\", err)\n\t\t}\n\t}\n\tif input.DurationMax != \"\" {\n\t\tdurationMax, err = time.ParseDuration(input.DurationMax)\n\t\tif err != nil {\n\t\t\treturn querysvc.TraceQueryParams{}, fmt.Errorf(\"invalid duration_max: %w\", err)\n\t\t}\n\t}\n\n\tif durationMin > 0 && durationMax > 0 && durationMax < durationMin {\n\t\treturn querysvc.TraceQueryParams{}, errors.New(\"duration_max must be greater than duration_min\")","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/search_traces.go#L124-L160","documentation":"search_traces' buildQuery parses start_time_min and start_time_max into minStartTime/maxStartTime. If max is set and falls before min, the range is empty and the query would never match anything, so buildQuery refuses it with \"start_time_max must be after start_time_min\". It is a caller-side range validation error, raised before storage is queried.","triggerScenarios":"Calling the search_traces MCP tool with start_time_max earlier than start_time_min, e.g. swapping the two values, passing relative times whose ordering inverts across time zones, or computing the max from a clock that lags the min.","commonSituations":"Swapped min/max arguments in an MCP tool call; building the range from 'last N hours' logic applied to the wrong bound; a client serializing datetimes in different time zones so the parsed max precedes the parsed min.","solutions":["Swap or correct the start_time_min/start_time_max values so max is strictly after min.","If times are relative, compute both from a single clock reading before formatting them.","Ensure both timestamps are formatted in the same time zone / RFC3339 offset the tool expects.","Validate ordering client-side before invoking the tool to get a clearer message tied to your own context."],"exampleFix":"// before\ninput := types.SearchTracesInput{\n    StartTimeMin: \"2024-06-02T00:00:00Z\",\n    StartTimeMax: \"2024-06-01T00:00:00Z\", // inverted\n}\n// after\nminT, _ := time.Parse(time.RFC3339, \"2024-06-01T00:00:00Z\")\nmaxT, _ := time.Parse(time.RFC3339, \"2024-06-02T00:00:00Z\")\nif !maxT.After(minT) {\n    minT, maxT = maxT, minT // or fix the caller\n}","handlingStrategy":"validation","validationCode":"minT, _ := time.Parse(time.RFC3339, input.StartTimeMin)\nmaxT, _ := time.Parse(time.RFC3339, input.StartTimeMax)\nif !maxT.After(minT) {\n    return fmt.Errorf(\"start_time_max (%s) must be after start_time_min (%s)\", maxT, minT)\n}","typeGuard":null,"tryCatchPattern":"out, err := handler.Handle(ctx, in)\nif err != nil {\n    if strings.Contains(err.Error(), \"start_time_max must be after\") {\n        // swap or clamp the range and retry once\n        in.StartTimeMin, in.StartTimeMax = in.StartTimeMax, in.StartTimeMin\n        return handler.Handle(ctx, in)\n    }\n    return err\n}","preventionTips":["Always derive both bounds from a single clock reading.","Normalize all timestamps to UTC RFC3339 before sending.","Unit-test any code that builds time ranges for the min<max invariant."],"tags":["mcp","input-validation","time-range"],"backgroundTag":"invalid-date-range","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}