FasterXML/jackson-databind · error · IllegalStateException
AnnotationIntrospector returned key deserializer definition
Error message
AnnotationIntrospector returned key deserializer definition of type {}; expected type KeyDeserializer or Class<KeyDeserializer> instead What it means
Thrown from BasicDeserializerFactory._valueInstantiatorInstance when the object returned for a ValueInstantiator definition is neither a ValueInstantiator instance nor a Class. IMPORTANT: the message text says 'key deserializer definition' but the code path enforces the VALUE-INSTANTIATOR contract — the wording is a copy-paste bug; the actual fault is in value-instantiator resolution (e.g. via @JsonValueInstantiator or a custom introspector).
Source
Thrown at src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java:285
}
return creators.constructValueInstantiator(ctxt);
}
public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config,
Annotated annotated, Object instDef)
{
if (instDef == null) {
return null;
}
ValueInstantiator inst;
if (instDef instanceof ValueInstantiator valueInstantiator) {
return valueInstantiator;
}
if (!(instDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+instDef.getClass().getName()
+"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
}
Class<?> instClass = (Class<?>)instDef;
if (ClassUtil.isBogusClass(instClass)) {
return null;
}
if (!ValueInstantiator.class.isAssignableFrom(instClass)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "+instClass.getName()
+"; expected Class<ValueInstantiator>");
}
HandlerInstantiator hi = config.getHandlerInstantiator();
if (hi != null) {
inst = hi.valueInstantiatorInstance(config, annotated, instClass);
if (inst != null) {
return inst;
}
}View on GitHub (pinned to a50c7d2a1d)
Solutions
- Audit the introspector: the value-instantiator resolution must return null, a ValueInstantiator instance, or a Class<? extends ValueInstantiator>.
- Do NOT trust the 'key deserializer' wording in the message — it is misleading; investigate the ValueInstantiator/@JsonValueInstantiator path first.
- If resolving by name, resolve to the raw Class before returning; never return a Type or TypeReference.
Example fix
// before: introspector returns a TypeReference wrapper instead of a Class
public Object findValueInstantiator(Annotated a) {
return new TypeReference<MyInstantiator>(){}; // not a Class -> throws (with misleading msg)
}
// after
public Object findValueInstantiator(Annotated a) {
return MyInstantiator.class; // raw Class<? extends ValueInstantiator>
} Defensive patterns
Strategy: type-guard
Type guard
static boolean isLegalInstantiatorDef(Object def) {
return def == null
|| def instanceof ValueInstantiator
|| (def instanceof Class<?> c && ValueInstantiator.class.isAssignableFrom(c));
} Prevention
- The message says 'key deserializer' but the fault is in value-instantiator resolution — check that path first.
- Keep introspector handler-returning methods to the documented null|instance|Class contract.
- Never return a Type/TypeReference/String for a value-instantiator definition.
When it happens
Trigger: A custom AnnotationIntrospector's value-instantiator resolution returns an unexpected type — not a ValueInstantiator, not a Class (e.g. a Type, a TypeReference, a String, or a framework wrapper). Reachable through @JsonCreator/@JsonValueInstantiator handling or a misbehaving HandlerInstantiator.
Common situations: A custom introspection framework intercepting value-instantiator resolution and returning its own wrapper type; an annotation that resolves to a Type instead of a Class; a library upgrade where the introspector method signature/contract changed.
Related errors
- AnnotationIntrospector returned Class {}; expected Class<Val
- AnnotationIntrospector returned deserializer definition of t
- AnnotationIntrospector returned key deserializer definition
- AnnotationIntrospector returned Converter definition of type
- AnnotationIntrospector returned Class {}; expected Class<Con
AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06).
Data as JSON: /api/errors/786c30b67d4a1687.
Report an issue: GitHub.