FasterXML/jackson-databind · error · IllegalStateException
`AnnotationIntrospector` returned `Converter` definition of
Error message
`AnnotationIntrospector` returned `Converter` definition of type {}; expected type `Converter` or `Class<Converter>` instead What it means
Thrown by MapperConfigBase._createConverter when resolving a converter definition: the AnnotationIntrospector returned an object that is neither a Converter instance nor a Class. The introspector contract requires one of those two shapes (or null), so any other type is a bug in the introspector or a misbehaving annotation mapping.
Source
Thrown at src/main/java/tools/jackson/databind/cfg/MapperConfigBase.java:739
/*
/**********************************************************************
/* Helper methods for implementations
/**********************************************************************
*/
@SuppressWarnings("unchecked")
protected Converter<Object,Object> _createConverter(Annotated annotated,
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
- Audit the custom AnnotationIntrospector: every converter-returning method must yield null, a Converter<?,?> instance, or a Class<? extends Converter>.
- If resolving by name, look up the actual Class (Class.forName / registry) and return the raw Class, not a wrapper or String.
- Add a contract unit test asserting the introspector's converter method returns one of the legal shapes on every code path.
Example fix
// before
public class MyIntrospector extends AnnotationIntrospector {
@Override public Object findDeserializationConverter(Annotated a) {
return "com.example.MyConverter"; // String -> throws
}
}
// after
public class MyIntrospector extends AnnotationIntrospector {
@Override public Object findDeserializationConverter(Annotated a) {
return MyConverter.class; // Class<? extends Converter>
}
} Defensive patterns
Strategy: type-guard
Type guard
static boolean isLegalConverterDef(Object def) {
return def == null
|| def instanceof Converter<?,?>
|| (def instanceof Class<?> c && Converter.class.isAssignableFrom(c));
} Prevention
- Custom introspector methods that resolve handlers must return instances or Class objects, never strings or wrapper types.
- Unit-test introspector return values against the documented null|instance|Class contract.
- Keep a single helper that validates every handler-def shape and route all introspector returns through it.
When it happens
Trigger: A custom AnnotationIntrospector overriding findSerializationConverter/findDeserializationConverter (or equivalent) returns a String, a Type, a TypeReference, or some framework wrapper object instead of a Converter or Class<Converter>.
Common situations: A custom framework introspection layer that maps its own annotations to Jackson handlers but wraps refs in its own types; a buggy mixin/plugin intercepting converter resolution; returning a fully-qualified class name String instead of a Class.
Related errors
- AnnotationIntrospector returned Converter definition of type
- AnnotationIntrospector returned Class {}; expected Class<Con
- AnnotationIntrospector returned `Class<{}>`; expected `Class
- AnnotationIntrospector.{}() returned value of type {}: expec
- AnnotationIntrospector returned key deserializer definition
AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06).
Data as JSON: /api/errors/371d132f044e1f94.
Report an issue: GitHub.