FasterXML/jackson-databind · error · IllegalStateException
AnnotationIntrospector returned Class {}; expected Class<Con
Error message
AnnotationIntrospector returned Class {}; expected Class<Converter> What it means
DatabindContext.converterInstance() received a Class object from the AnnotationIntrospector for a converter definition, but that Class does not implement the Converter interface. The library can only instantiate classes that are assignable to Converter, so it rejects the definition with the offending class name. This is a stricter, more specific sibling of the 'wrong type' error (index 0) and almost always points at an annotation or introspector pointing to the wrong class.
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 a50c7d2a1d)
Solutions
- Open the named class and confirm whether it 'implements Converter'. If not, either make it implement Converter or move the reference to the correct annotation attribute (using= for ValueSerializer, contentUsing=, etc.).
- Check the @JsonSerialize/@JsonDeserialize annotation site cited in context for a wrong class reference.
- If a mixin is involved, verify the mixin's converter attribute points at a real Converter class.
- Recompile to catch the type mismatch at compile time where possible (use Class<? extends Converter<?,?>> typed fields in your own config holders).
Example fix
// before
@JsonSerialize(converter = MySerializer.class) // MySerializer extends ValueSerializer, NOT Converter
public Value getValue() { ... }
// after
@JsonSerialize(using = MySerializer.class)
public Value getValue() { ... } Defensive patterns
Strategy: type-guard
Validate before calling
// Before returning a converter Class, verify it implements Converter
static Class<?> checkedConverterClass(Class<?> c) {
if (c != null && !Converter.class.isAssignableFrom(c)) {
throw new IllegalStateException(c.getName() + " is not a Converter");
}
return c;
} Type guard
boolean isConverterClass(Class<?> c) {
return c != null && Converter.class.isAssignableFrom(c);
} Prevention
- Distinguish 'converter' (Converter) from 'using' (ValueSerializer/ValueDeserializer) when annotating.
- Use strongly-typed Class<? extends Converter<?,?>> fields in your config holders.
- Compile-time-check annotation references by keeping the referenced class in the same module.
When it happens
Trigger: An annotation like @JsonSerialize(converter = SomeClass.class) or @JsonDeserialize(converter = SomeClass.class) where SomeClass is not a Converter; or a custom introspector returning a Class that is a serializer/deserializer, a factory, or an unrelated POJO instead of a Converter. Also triggered by mixins that remap the converter attribute to an arbitrary class.
Common situations: Confusing 'converter' with 'using' (which expects a ValueSerializer/ValueDeserializer class); copy-paste of a class reference after refactoring where the class lost its Converter implementation; 2.x-to-3.x migration where a class was renamed or its generics changed and no longer matches; using a @JsonSerialize.converter on a field whose target is a basic type with no custom Converter written.
Related errors
- AnnotationIntrospector returned Converter definition of type
- `AnnotationIntrospector` returned `Converter` definition of
- AnnotationIntrospector returned `Class<{}>`; expected `Class
- Invalid abstract type resolution from {} to {}: latter is no
- Failed to parse Date value '%s': %s
AI-assisted analysis of FasterXML/jackson-databind@a50c7d2a1d (2026-08-06).
Data as JSON: /api/errors/e77c525839480c12.
Report an issue: GitHub.