FasterXML/jackson-databind · error · IllegalStateException
AnnotationIntrospector returned Class
Error message
AnnotationIntrospector returned Class {}; expected Class<Converter> What it means
Thrown by DatabindContext.converterInstance() when the object returned by AnnotationIntrospector is a Class, but that Class does not implement the Converter interface. The @JsonSerialize(converter=X.class) or @JsonDeserialize(converter=X.class) annotation requires X to be a valid Converter implementation.
Solutions
- Verify the class referenced in converter=... implements tools.jackson.databind.util.Converter.
- If you meant to use a custom serializer/deserializer, use the using= or contentUsing= attribute instead of converter=.
- Make the referenced class extend StdConverter<IN,OUT> for a zero-boilerplate Converter implementation.
Example fix
// before @JsonSerialize(converter = MySerializer.class) // MySerializer extends ValueSerializer, not Converter // after @JsonSerialize(using = MySerializer.class) // correct attribute for a ValueSerializer
Defensive patterns
Strategy: validation
Validate before calling
// Verify a converter class before using it in an annotation
Class<?> converterClass = MyConverter.class;
if (!Converter.class.isAssignableFrom(converterClass)) {
throw new IllegalArgumentException(converterClass + " does not implement Converter");
} Type guard
boolean isValidConverterClass(Class<?> cls) {
return cls != null && Converter.class.isAssignableFrom(cls);
} Prevention
- Distinguish @JsonSerialize(using=Serializer.class) from @JsonSerialize(converter=Converter.class).
- Have Converter classes extend StdConverter<IN,OUT> for compile-time safety.
- Run a quick smoke test (serialize/deserialize one instance) on every annotated type.
When it happens
Trigger: Annotating a property or type with @JsonSerialize(converter=SomeClass.class) or @JsonDeserialize(converter=SomeClass.class) where SomeClass does not implement tools.jackson.databind.util.Converter. Also triggered by a custom AnnotationIntrospector that returns a Class that is not a Converter subtype.
Common situations: Copy-paste errors where a serializer/deserializer class is accidentally used as a converter. Refactoring where a class stopped implementing Converter. Confusion between @JsonSerialize(using=...) and @JsonSerialize(converter=...).
Related errors
- AnnotationIntrospector returned Converter definition of type
- AnnotationIntrospector returned `Class<
- `AnnotationIntrospector` returned `Converter` definition of…
- _anySetter already set to non-null
- Cannot deserialize Singleton container from "+size+" entries
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/e77c525839480c12.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/tools/jackson/databind/DatabindContext.java:470
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 "
+converterDef.getClass().getName()+"; 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 "
+converterClass.getName()+"; expected Class<Converter>");
}
final MapperConfig<?> config = getConfig();
HandlerInstantiator hi = config.getHandlerInstantiator();
Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(config, annotated, converterClass);
if (conv == null) {
conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
config.canOverrideAccessModifiers());
}
return (Converter<Object,Object>) conv;
}
/*
/**********************************************************************
/* Misc config access
/**********************************************************************
*/
View on GitHub (pinned to 87876ca5c0)