FasterXML/jackson-databind · error · IllegalStateException
AnnotationIntrospector returned Class ${deserClass.getName()
Error message
AnnotationIntrospector returned Class ${deserClass.getName()}; expected Class<KeyDeserializer> What it means
Thrown when keyDeserializerInstance receives a Class as the key-deserializer definition, but that Class does not extend KeyDeserializer. This is the class-typed sibling of error 41: the value is a Class (so it passed the first check) but it is the wrong kind of class.
Source
Thrown at src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java:293
}
KeyDeserializer deser;
if (deserDef instanceof KeyDeserializer keyDeserializer) {
deser = keyDeserializer;
} else {
if (!(deserDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+deserDef.getClass().getName()
+"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
}
Class<?> deserClass = (Class<?>)deserDef;
// there are some known "no class" markers to consider too:
if (deserClass == KeyDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) {
return null;
}
if (!KeyDeserializer.class.isAssignableFrom(deserClass)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName()
+"; expected Class<KeyDeserializer>");
}
HandlerInstantiator hi = _config.getHandlerInstantiator();
deser = (hi == null) ? null : hi.keyDeserializerInstance(_config, ann, deserClass);
if (deser == null) {
deser = (KeyDeserializer) ClassUtil.createInstance(deserClass,
_config.canOverrideAccessModifiers());
}
}
// First: need to resolve
deser.resolve(this);
return deser;
}
/*
/**********************************************************************
/* Extended API, read methods
/**********************************************************************View on GitHub (pinned to 87876ca5c0)
Solutions
- Ensure the referenced class extends tools.jackson.databind.KeyDeserializer and overrides deserializeKey(String, DeserializationContext).
- If you intended a value deserializer, register it via @JsonDeserialize(contentUsing = ...) instead of keyUsing.
- Return KeyDeserializer.None.class or null to disable the custom key deserializer.
Example fix
// before @JsonDeserialize(keyUsing = MyValueDeserializer.class) Map<String,V> map; // MyValueDeserializer extends ValueDeserializer, not KeyDeserializer // after @JsonDeserialize(keyUsing = MyKeyDeserializer.class) Map<String,V> map; // MyKeyDeserializer extends KeyDeserializer
Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = (Class<?>) def;
if (c != null && c != KeyDeserializer.None.class
&& !KeyDeserializer.class.isAssignableFrom(c)) {
throw new IllegalStateException("Not a KeyDeserializer: " + c);
} Type guard
static boolean isKeyDeserializerClass(Class<?> c) {
return c != null && c != KeyDeserializer.None.class
&& KeyDeserializer.class.isAssignableFrom(c);
} Try / catch
try { mapper.readValue(json, mapType); }
catch (IllegalStateException e) {
if (e.getMessage().contains("expected Class<KeyDeserializer>")) {
// referenced class must extend KeyDeserializer
} else throw e;
} Prevention
- Key deserializers extend KeyDeserializer; value deserializers extend ValueDeserializer. Confirm before referencing in @JsonDeserialize(keyUsing=...).
- Keep a project-wide list of key vs value deserializer classes to avoid mix-ups.
When it happens
Trigger: @JsonDeserialize(keyUsing = SomeClass.class) where SomeClass does not extend KeyDeserializer; an AnnotationIntrospector returning Class<ValueDeserializer> or any unrelated Class for a map key deserializer.
Common situations: Reusing a value deserializer class for map keys; forgetting that key deserializers must extend KeyDeserializer specifically; refactor that changed a class's hierarchy after it was referenced in annotations.
Related errors
- AnnotationIntrospector returned key deserializer definition
- AnnotationIntrospector returned `Class<${deserClass.getName(
- AnnotationIntrospector returned Converter definition of type
- Failed to parse Date value '%s': %s
- Cannot pass null KeyDeserializers
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/8b7980714a41aa88.
Report an issue: GitHub.