{"record":{"id":"414ffa2704862b87","repo":"halo-dev/halo","slug":"cannot-convert-key-to-type","errorCode":null,"errorMessage":"Cannot convert key: {} to type: {}","messagePattern":"Cannot convert key: (.+?) to type: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"application/src/main/java/run/halo/app/extension/index/query/QueryVisitor.java","lineNumber":221,"sourceCode":"\n        private <K extends Comparable<K>> Set<String> isNullQuery(String indexName, boolean negated) {\n            var index = this.<K>getValueIndexQuery(indexName);\n            if (negated) {\n                return index.isNotNull();\n            }\n            return index.isNull();\n        }\n\n        private <K extends Comparable<K>> Set<String> betweenQuery(\n                String indexName,\n                Object fromKey,\n                boolean fromInclusive,\n                Object toKey,\n                boolean toInclusive,\n                boolean negated) {\n            var index = this.<K>getValueIndexQuery(indexName);\n            if (!conversionService.canConvert(fromKey.getClass(), index.getKeyType())) {\n                throw new IllegalArgumentException(\n                        \"Cannot convert key: \" + fromKey + \" to type: \" + index.getKeyType());\n            }\n            if (!conversionService.canConvert(toKey.getClass(), index.getKeyType())) {\n                throw new IllegalArgumentException(\"Cannot convert key: \" + toKey + \" to type: \" + index.getKeyType());\n            }\n            if (negated) {\n                return index.notBetween(\n                        conversionService.convert(fromKey, index.getKeyType()),\n                        fromInclusive,\n                        conversionService.convert(toKey, index.getKeyType()),\n                        toInclusive);\n            } else {\n                return index.between(\n                        conversionService.convert(fromKey, index.getKeyType()),\n                        fromInclusive,\n                        conversionService.convert(toKey, index.getKeyType()),\n                        toInclusive);\n            }","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/extension/index/query/QueryVisitor.java#L203-L239","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nquery.between(\"creationTimestamp\", \"2024-01-01\", ...); // String vs date index\n\n// after\nquery.between(\"creationTimestamp\", Instant.parse(\"2024-01-01T00:00:00Z\"), ...);","handlingStrategy":"validation","validationCode":"Class<?> keyType = valueIndexQuery.getKeyType();\nif (!conversionService.canConvert(fromBound.getClass(), keyType)) {\n    throw new IllegalArgumentException(\"BETWEEN from-bound not convertible to \" + keyType);\n}","typeGuard":"static boolean boundConvertible(ConversionService cs, Object bound, Class<?> keyType) {\n    return bound != null && cs.canConvert(bound.getClass(), keyType);\n}","tryCatchPattern":"try {\n    results = visitor.betweenQuery(idx, fromBound, true, toBound, true, false);\n} catch (IllegalArgumentException e) {\n    // tell the caller the bound type is incompatible; suggest the index key type\n    throw new ServerWebInputException(\"Invalid BETWEEN bound type for index \" + idx, null, e);\n}","preventionTips":["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."],"tags":["query","index","type-conversion","filter-expression"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}