{"record":{"id":"5aa7f76776eaeaa1","repo":"OrchardCMS/OrchardCore","slug":"lower-and-upper-bound-range-types-don-t-match-5aa7f7","errorCode":null,"errorMessage":"Lower and upper bound range types don't match","messagePattern":"Lower and upper bound range types don't match","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.Lucene.Core/QueryProviders/RangeQueryProvider.cs","lineNumber":61,"sourceCode":"                            break;\n                        case \"lt\":\n                            lt = element.Value;\n                            nodeKind = lt.GetValueKind();\n                            break;\n                        case \"lte\":\n                            lt = element.Value;\n                            nodeKind = lt.GetValueKind();\n                            includeUpper = true;\n                            break;\n                        case \"boost\":\n                            boost = element.Value.Value<float>();\n                            break;\n                    }\n                }\n\n                if (gt != null && lt != null && gt.GetValueKind() != lt.GetValueKind())\n                {\n                    throw new ArgumentException(\"Lower and upper bound range types don't match\");\n                }\n\n                switch (nodeKind)\n                {\n                    case JsonValueKind.Number:\n                        if (gt.AsValue().TryGetValue<long>(out var minInt) &&\n                            lt.AsValue().TryGetValue<long>(out var maxInt))\n                        {\n                            rangeQuery = NumericRangeQuery.NewInt64Range(field, minInt, maxInt, includeLower, includeUpper);\n                        }\n                        else if (gt.AsValue().TryGetValue<double>(out var minFloat) &&\n                            lt.AsValue().TryGetValue<double>(out var maxFloat))\n                        {\n                            rangeQuery = NumericRangeQuery.NewDoubleRange(field, minFloat, maxFloat, includeLower, includeUpper);\n                        }\n                        else\n                        {\n                            throw new ArgumentException($\"Unsupported range value type: {type}\");","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.Lucene.Core/QueryProviders/RangeQueryProvider.cs#L43-L79","documentation":"RangeQueryProvider.CreateQuery supports range queries where the lower (gt/gte) and upper (lt/lte) bounds are of the same JSON type (both numbers or both strings). If both bounds are present but their JSON value kinds differ, e.g. a number lower bound with a string upper bound, it throws ArgumentException because Lucene range queries need homogeneous bound types.","triggerScenarios":"A range node like {\"field\": {\"type\": \"range\", \"gt\": 5, \"lt\": \"10\"}} — gt is a number but lt is a string, so GetValueKind() differs.","commonSituations":"Mixing quoted and unquoted numbers in hand-edited query JSON; dates given as strings with numeric bounds; query built by concatenating user-supplied filters of different types.","solutions":["Make both bounds the same JSON type — quote both or unquote both, e.g. {\"gt\": \"5\", \"lt\": \"10\"} or {\"gt\": 5, \"lt\": 10}.","If the field is a date/text field, express both bounds as strings; if numeric, both as numbers.","Normalize bound values in the code that assembles the query before calling CreateQuery."],"exampleFix":"// before\n{\"Price\": {\"type\": \"range\", \"gt\": 10, \"lt\": \"100\"}}\n// after\n{\"Price\": {\"type\": \"range\", \"gt\": 10, \"lt\": 100}}","handlingStrategy":"validation","validationCode":"var gt = obj[\"gt\"] ?? obj[\"gte\"]; var lt = obj[\"lt\"] ?? obj[\"lte\"]; if (gt != null && lt != null && gt.GetValueKind() != lt.GetValueKind()) throw new InvalidOperationException(\"Range bounds must share the same JSON type\");","typeGuard":"static bool BoundsKindMatch(JsonNode obj) { var g = obj[\"gt\"] ?? obj[\"gte\"]; var l = obj[\"lt\"] ?? obj[\"lte\"]; return g == null || l == null || g.GetValueKind() == l.GetValueKind(); }","tryCatchPattern":"try { var q = provider.CreateQuery(node); } catch (ArgumentException ex) when (ex.Message.Contains(\"range types don't match\")) { // coerce both bounds to a common type and retry }","preventionTips":["Normalize numeric bounds to unquoted numbers and text/date bounds to quoted strings.","Build both bounds from the same typed variable in query-builder code.","Add a pre-submit validation step for range filters in the UI."],"tags":["lucene","range-query","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}