{"record":{"id":"392b816ed3473b0c","repo":"jaegertracing/jaeger","slug":"duration-max-must-be-greater-than-duration-min","errorCode":null,"errorMessage":"duration_max must be greater than duration_min","messagePattern":"duration_max must be greater than duration_min","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/search_traces.go","lineNumber":160,"sourceCode":"\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\")\n\t}\n\n\tconst defaultSearchDepth = 10\n\tsearchDepth := input.SearchDepth\n\tif searchDepth <= 0 {\n\t\tsearchDepth = defaultSearchDepth\n\t}\n\tif searchDepth > h.maxResults {\n\t\tsearchDepth = h.maxResults\n\t}\n\n\tattributes := pcommon.NewMap()\n\tfor key, value := range input.Attributes {\n\t\tattributes.PutStr(key, value)\n\t}\n\tif input.WithErrors {\n\t\tattributes.PutStr(\"error\", \"true\")\n\t}","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/internal/handlers/search_traces.go#L142-L178","documentation":"search_traces' buildQuery parses duration_min and duration_max with time.ParseDuration and rejects a query where both are set and duration_max is smaller than duration_min. Such a duration window is empty and can never match, so the error prevents a guaranteed-empty search. Like the time-range check, it fires before any storage call.","triggerScenarios":"Calling the search_traces MCP tool with duration_max < duration_min (e.g. duration_min=\"1s\", duration_max=\"500ms\"), or passing values in mismatched units so the parsed max ends up smaller (\"2ms\" vs \"1s\").","commonSituations":"Swapped min/max fields in the request; unit confusion where one value is milliseconds and the other seconds (Go durations require explicit units like 100ms, 2s); a dashboard passing user-supplied bounds without validating order.","solutions":["Swap the duration_min/duration_max values so max is greater than min.","Double-check the Go duration units on both values (ms vs s vs m) — unit mistakes silently change magnitude.","Parse both values with time.ParseDuration client-side and assert max > min before calling the tool."],"exampleFix":"// before\ntypes.SearchTracesInput{DurationMin: \"2s\", DurationMax: \"500ms\"} // inverted\n// after\nminD, _ := time.ParseDuration(\"500ms\")\nmaxD, _ := time.ParseDuration(\"2s\")\nif minD >= maxD {\n    minD, maxD = maxD, minD\n}\ntypes.SearchTracesInput{DurationMin: minD.String(), DurationMax: maxD.String()}","handlingStrategy":"validation","validationCode":"minD, err := time.ParseDuration(input.DurationMin); if err != nil { return err }\nmaxD, err := time.ParseDuration(input.DurationMax); if err != nil { return err }\nif maxD < minD {\n    return fmt.Errorf(\"duration_max (%s) must be >= duration_min (%s)\", maxD, minD)\n}","typeGuard":null,"tryCatchPattern":"out, err := handler.Handle(ctx, in)\nif err != nil {\n    if strings.Contains(err.Error(), \"duration_max must be greater\") {\n        in.DurationMin, in.DurationMax = in.DurationMax, in.DurationMin\n        return handler.Handle(ctx, in)\n    }\n    return err\n}","preventionTips":["Write duration bounds with explicit units always (500ms, 2s) — never bare numbers.","Validate min<max for every duration window at the config/UI layer.","When converting between units (ms<->s), parse-and-reformat via time.Duration instead of manual arithmetic."],"tags":["mcp","input-validation","duration"],"backgroundTag":"invalid-parameter-range","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}