OrchardCMS/OrchardCore · error · ArgumentException
Lower and upper bound range types don't match
Error message
Lower and upper bound range types don't match
What it means
RangeFilterProvider.CreateFilteredQuery supports range filters with a lower bound (gt/gte) and an upper bound (lt/lte). When both bounds are present their JSON value kinds must match (both numbers, or both strings, etc.); if they differ, e.g. gt is a number and lt is a string, the provider throws ArgumentException("Lower and upper bound range types don't match") because Lucene range queries need a consistent type for min and max.
Solutions
- Make both bounds the same JSON type: use {"gt": 10, "lt": 100} or {"gt": "a", "lt": "b"}, never a mix.
- If bounds are dates, quote both: {"gte": "2024-01-01", "lte": "2024-12-31"}.
- In dynamic query builders, coerce/serialize both bounds from the same source type before building the filter.
- Validate that gt/gte and lt/lte have equal JsonValueKind before calling CreateFilteredQuery.
Example fix
// before
{"range": {"Price": {"gt": 10, "lt": "100"}}}
// after
{"range": {"Price": {"gt": 10, "lt": 100}}} Defensive patterns
Strategy: validation
Validate before calling
var obj = rangeValue.AsObject();
bool hasGt = obj.TryGetPropertyValue("gt", out var gt) || obj.TryGetPropertyValue("gte", out gt);
bool hasLt = obj.TryGetPropertyValue("lt", out var lt) || obj.TryGetPropertyValue("lte", out lt);
if (hasGt && hasLt && gt.ValueKind != lt.ValueKind)
{
throw new InvalidOperationException(
$"Range bounds must share the same JSON type (got {gt.ValueKind} vs {lt.ValueKind}).");
} Type guard
static bool BoundsKindMatch(JsonElement? gt, JsonElement? lt) =>
gt is null || lt is null || gt.GetValueKind() == lt.GetValueKind(); Try / catch
try
{
var query = provider.CreateFilteredQuery(context);
}
catch (ArgumentException ex) when (ex.Message.Contains("types don't match"))
{
_logger.LogError(ex, "Mixed range bound types: {Json}", filterJson);
throw new QueryValidationException("Range lower/upper bounds must be the same type.", ex);
} Prevention
- Quote date bounds consistently on both sides.
- In dynamic builders, derive both bounds from one typed value.
- Check gt/gte and lt/lte JsonValueKind equality before executing.
When it happens
Trigger: Calling CreateFilteredQuery with {"range": {"Price": {"gt": 10, "lt": "100"}}} or any mix like {"gte": "2024-01-01", "lt": 5} where the lower and upper bound JSON kinds differ (number vs string, number vs boolean).
Common situations: Dates supplied as strings on one bound but parsed as numbers on the other; one bound quoted and the other not when hand-editing JSON; dynamic query builders formatting only one bound; data migrated between string-typed and numeric-typed fields.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Unsupported range value type
- Invalid range query
- Lower and upper bound range types don't match
- Invalid query
- Missing value in match phrase query
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/3a5877c604ce1c37.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore.Lucene.Core/QueryProviders/Filters/RangeFilterProvider.cs:68
break;
case "lt":
lt = element.Value;
nodeKind = lt.GetValueKind();
break;
case "lte":
lt = element.Value;
nodeKind = lt.GetValueKind();
includeUpper = true;
break;
case "boost":
boost = element.Value.Value<float>();
break;
}
}
if (gt is not null && lt is not null && gt.GetValueKind() != lt.GetValueKind())
{
throw new ArgumentException("Lower and upper bound range types don't match");
}
switch (nodeKind)
{
case JsonValueKind.Number:
if (gt.AsValue().TryGetValue<long>(out var minInt) &&
lt.AsValue().TryGetValue<long>(out var maxInt))
{
rangeQuery = NumericRangeQuery.NewInt64Range(field, minInt, maxInt, includeLower, includeUpper);
}
else if (gt.AsValue().TryGetValue<double>(out var minFloat) &&
lt.AsValue().TryGetValue<double>(out var maxFloat))
{
rangeQuery = NumericRangeQuery.NewDoubleRange(field, minFloat, maxFloat, includeLower, includeUpper);
}
else
{
throw new ArgumentException($"Unsupported range value type: {type}");View on GitHub (pinned to 4306c0717f)