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 by BasicDeserializerFactory._valueInstantiatorInstance when an AnnotationIntrospector returned a value-instantiator definition whose runtime type is neither a ValueInstantiator instance nor a Class. NOTE: the message text in the source says 'key deserializer definition' / 'KeyDeserializer', but the code path actually handles @JsonValueInstantiator / ValueInstantiator resolution -- the wording is misleading; the trigger is a malformed ValueInstantiator definition returned by a custom introspector.
Solutions
- Fix the custom AnnotationIntrospector to return a ValueInstantiator instance or a Class<? extends ValueInstantiator> for the value-instantiator slot (or null).
- Check @JsonValueInstantiator on the target type points to a Class<ValueInstantiator> and is not being rewritten by a mixin/introspector.
- If you do not need a custom instantiator, remove the annotation/introspector override.
Example fix
// before: introspector returns a String for the instantiator slot
@Override
public Object findValueInstantiator(Annotated a) {
return a.getName();
}
// after
@Override
public Object findValueInstantiator(Annotated a) {
return MyValueInstantiator.class;
} Defensive patterns
Strategy: type-guard
Validate before calling
Object def = introspector.findValueInstantiator(annotated);
if (def != null && !(def instanceof ValueInstantiator) && !(def instanceof Class)) {
throw new IllegalStateException("Bad value-instantiator definition: " + def.getClass());
} Type guard
boolean isInstantiatorDef(Object d) {
return d == null || d instanceof ValueInstantiator || d instanceof Class;
} Prevention
- Custom introspectors must return ValueInstantiator, Class<? extends ValueInstantiator>, or null from the instantiator hook.
- Note the source message text says 'key deserializer' but the trigger is the value-instantiator slot -- do not be misled.
- Unit-test introspector return values for each annotated type.
When it happens
Trigger: A custom AnnotationIntrospector returning an arbitrary non-Class object from the value-instantiator lookup (used for @JsonValueInstantiator), or annotation data that resolves to something other than a ValueInstantiator or Class.
Common situations: Custom introspector implementation returning the wrong object type for the value-instantiator slot; a plugin/framework remapping annotations incorrectly; copying an introspector pattern from a key-deserializer hook into the value-instantiator hook.
Related errors
- AnnotationIntrospector returned Class
- AnnotationIntrospector returned `Class<
- `AnnotationIntrospector` returned `Converter` definition of…
- AnnotationIntrospector returned deserializer definition of…
- Cannot pass null resolver
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/786c30b67d4a1687.
Report an issue: GitHub.
Appendix: 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 87876ca5c0)