FasterXML/jackson-databind · error · IllegalStateException
AnnotationIntrospector returned `Class<{}>`; expected `Class
Error message
AnnotationIntrospector returned `Class<{}>`; expected `Class<Converter>` What it means
Thrown by MapperConfigBase._createConverter when the introspector returns a Class for a converter, but that Class does not implement Converter. Jackson can only instantiate converter classes that satisfy the Converter interface, so any other class is rejected.
Source
Thrown at src/main/java/tools/jackson/databind/cfg/MapperConfigBase.java:748
Object converterDef)
{
if (converterDef == null) {
return null;
}
if (converterDef instanceof Converter<?,?>) {
return (Converter<Object,Object>) converterDef;
}
if (!(converterDef instanceof Class)) {
throw new IllegalStateException("`AnnotationIntrospector` returned `Converter` definition of type "
+ClassUtil.classNameOf(converterDef)+"; expected type `Converter` or `Class<Converter>` instead");
}
Class<?> converterClass = (Class<?>)converterDef;
// there are some known "no class" markers to consider too:
if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) {
return null;
}
if (!Converter.class.isAssignableFrom(converterClass)) {
throw new IllegalStateException("AnnotationIntrospector returned `Class<"
+ClassUtil.classNameOf(converterClass)+"`>; expected `Class<Converter>`");
}
HandlerInstantiator hi = getHandlerInstantiator();
Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(this, annotated, converterClass);
if (conv == null) {
conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
canOverrideAccessModifiers());
}
return (Converter<Object,Object>) conv;
}
}
View on GitHub (pinned to a50c7d2a1d)
Solutions
- Make the referenced class implement Converter<?,?> (override convert, getInputType, getOutputType).
- If you intended a different conversion mechanism, use a custom ValueDeserializer/ValueSerializer instead of a Converter.
- Verify the class referenced in @JsonSerialize(converter=...) / @JsonDeserialize(converter=...) actually implements Converter.
Example fix
// before
@JsonDeserialize(converter = StringReverser.class) // does NOT implement Converter
class StringReverser { String apply(String s){ return new StringBuilder(s).reverse().toString(); } }
// after
class StringReverser implements Converter<String,String> {
public String convert(String s){ return new StringBuilder(s).reverse().toString(); }
public JavaType getInputType(TypeFactory f){ return f.constructType(String.class); }
public JavaType getOutputType(TypeFactory f){ return f.constructType(String.class); }
} Defensive patterns
Strategy: type-guard
Type guard
static boolean isConverterClass(Class<?> c) {
return c != null && Converter.class.isAssignableFrom(c);
} Prevention
- Verify the class referenced by converter annotations implements Converter<?,?> before wiring.
- Keep converters in a dedicated package to avoid pointing annotations at look-alike classes.
- Distinguish Converter from ValueSerializer/ValueDeserializer when annotating.
When it happens
Trigger: An introspector (or @JsonDeserialize(converter=...) / @JsonSerialize(converter=...)) returns a Class that is not assignable to Converter<?,?> — e.g. a Function, a Supplier, a builder, or an unrelated bean.
Common situations: Pointing the converter annotation at the wrong class (a builder, a formatter, a DTO); a refactor that renamed or moved the converter without updating the annotation; confusing a Converter with a ValueSerializer/ValueDeserializer.
Related errors
- AnnotationIntrospector returned Converter definition of type
- AnnotationIntrospector returned Class {}; expected Class<Con
- `AnnotationIntrospector` returned `Converter` definition of
- AnnotationIntrospector returned Class {}; expected Class<Val
- AnnotationIntrospector returned `Class<{}>`; expected `Class
AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06).
Data as JSON: /api/errors/7a686bb919f747d0.
Report an issue: GitHub.