halo-dev/halo · error · IllegalArgumentException
Cannot convert key: {} to type: {}
Error message
Cannot convert key: {} to type: {} What it means
Thrown as IllegalArgumentException by QueryVisitor.betweenQuery when the 'from' bound of a BETWEEN query cannot be converted by the registered ConversionService into the target index key type. Halo evaluates list/query filter expressions against in-memory indices, so each literal must be coercible to the index's declared Comparable key type.
Source
Thrown at application/src/main/java/run/halo/app/extension/index/query/QueryVisitor.java:221
private <K extends Comparable<K>> Set<String> isNullQuery(String indexName, boolean negated) {
var index = this.<K>getValueIndexQuery(indexName);
if (negated) {
return index.isNotNull();
}
return index.isNull();
}
private <K extends Comparable<K>> Set<String> betweenQuery(
String indexName,
Object fromKey,
boolean fromInclusive,
Object toKey,
boolean toInclusive,
boolean negated) {
var index = this.<K>getValueIndexQuery(indexName);
if (!conversionService.canConvert(fromKey.getClass(), index.getKeyType())) {
throw new IllegalArgumentException(
"Cannot convert key: " + fromKey + " to type: " + index.getKeyType());
}
if (!conversionService.canConvert(toKey.getClass(), index.getKeyType())) {
throw new IllegalArgumentException("Cannot convert key: " + toKey + " to type: " + index.getKeyType());
}
if (negated) {
return index.notBetween(
conversionService.convert(fromKey, index.getKeyType()),
fromInclusive,
conversionService.convert(toKey, index.getKeyType()),
toInclusive);
} else {
return index.between(
conversionService.convert(fromKey, index.getKeyType()),
fromInclusive,
conversionService.convert(toKey, index.getKeyType()),
toInclusive);
}View on GitHub (pinned to d2f5165f9c)
Solutions
- Convert the from-bound to the index's key type on the caller side before building the query expression.
- Register a Converter<SourceType, KeyType> with the ConversionService used by the query layer.
- Verify the index name in the query actually points at the index with the expected key type.
- Align the literal format (e.g. ISO-8601 for dates, decimal string for numbers) with what the converter expects.
Example fix
// before
query.between("creationTimestamp", "2024-01-01", ...); // String vs date index
// after
query.between("creationTimestamp", Instant.parse("2024-01-01T00:00:00Z"), ...); Defensive patterns
Strategy: validation
Validate before calling
Class<?> keyType = valueIndexQuery.getKeyType();
if (!conversionService.canConvert(fromBound.getClass(), keyType)) {
throw new IllegalArgumentException("BETWEEN from-bound not convertible to " + keyType);
} Type guard
static boolean boundConvertible(ConversionService cs, Object bound, Class<?> keyType) {
return bound != null && cs.canConvert(bound.getClass(), keyType);
} Try / catch
try {
results = visitor.betweenQuery(idx, fromBound, true, toBound, true, false);
} catch (IllegalArgumentException e) {
// tell the caller the bound type is incompatible; suggest the index key type
throw new ServerWebInputException("Invalid BETWEEN bound type for index " + idx, null, e);
} Prevention
- Always pass bounds in the index's declared key type.
- Register converters for any custom key type before exposing it in queries.
- Validate query-string bounds against the schema before evaluation.
When it happens
Trigger: A ListOptions/field-selector query uses a BETWEEN operator whose lower bound literal type is incompatible with the index key type, e.g. querying a numeric/date index with a String bound that has no registered converter, or vice-versa.
Common situations: Passing raw String params from an HTTP query string against a numeric or temporal index; querying an index whose key type changed after a schema migration; a missing/removed ConversionService converter for a custom key type.
Related errors
- Unsupported operator: {}
- No value present for label key: {}
- Unsupported operator: {}
- No value present for label key: {}
- Duplicate key '%s' for extension '%s'
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/414ffa2704862b87.
Report an issue: GitHub.