FasterXML/jackson-databind · error · IllegalStateException
AnnotationIntrospector.${methodName}() returned value of typ
Error message
AnnotationIntrospector.${methodName}() returned value of type ${src.getClass().getName()}: expected type `ValueSerializer` or `Class<ValueSerializer>` instead What it means
Thrown by DeserializerCache._verifyAsClass when an AnnotationIntrospector method (e.g. findSerializer, findKeySerializer, findContentSerializer) returns a non-null object that is not a Class. Despite living in DeserializerCache, the message text and the helper's usage target serializer definitions; the method is shared for verifying serializer definitions sourced from annotations.
Source
Thrown at src/main/java/tools/jackson/databind/deser/DeserializerCache.java:576
}
// Second: map(-like) types may have value handler for key (but not type; keys are untyped)
if (t.isMapLikeType()) {
JavaType kt = t.getKeyType();
if (kt.getValueHandler() != null) {
return true;
}
}
}
return false;
}
private Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass)
{
if (src == null) {
return null;
}
if (!(src instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "
+src.getClass().getName()+": expected type `ValueSerializer` or `Class<ValueSerializer>` instead");
}
Class<?> cls = (Class<?>) src;
if (cls == noneClass || ClassUtil.isBogusClass(cls)) {
return null;
}
return cls;
}
/*
/**********************************************************************
/* Error reporting methods
/**********************************************************************
*/
protected ValueDeserializer<Object> _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)
{
// Let's try to figure out the reason, to give better error messagesView on GitHub (pinned to 87876ca5c0)
Solutions
- Return a Class<? extends ValueSerializer> or null from the introspector method named in the message.
- If you must supply an instance, register it directly via a Module/SerializerFactory or ObjectMapper API rather than through the introspector.
- Confirm you are on the Jackson 3.x introspector contract; 2.x returning patterns may differ.
Example fix
// before
@Override
public Object findSerializer(Annotated a) {
return new MySerializer(); // instance not allowed here
}
// after
@Override
public Object findSerializer(Annotated a) {
return MySerializer.class; // Class<? extends ValueSerializer>
} Defensive patterns
Strategy: validation
Validate before calling
Object v = introspector.findSerializer(annotated);
if (v != null && !(v instanceof Class)) {
throw new IllegalStateException("findSerializer must return Class or null, got " + v.getClass());
} Type guard
static boolean isSerializerClassDef(Object v) {
return v == null || (v instanceof Class<?> c && ValueSerializer.class.isAssignableFrom(c));
} Try / catch
try { mapper.writeValue(out, value); }
catch (IllegalStateException e) {
if (e.getMessage().contains("expected type `ValueSerializer` or `Class<ValueSerializer>`")) {
// introspector returned a non-Class; return a Class<? extends ValueSerializer>
} else throw e;
} Prevention
- Custom introspector find*Serializer methods return Class<? extends ValueSerializer> or null only.
- For instances, register via Module or SerializerFactory instead of the introspector.
When it happens
Trigger: A custom AnnotationIntrospector override (findSerializer / findKeySerializer / findContentSerializer / findNullSerializer) returning an instance, a String, or any non-Class object instead of a Class<? extends ValueSerializer> or null.
Common situations: Returning an instantiated ValueSerializer from an introspector whose contract expects a Class; an introspector written against an older Jackson API; returning a Class object wrapped in another type (e.g. TypeReference).
Related errors
- AnnotationIntrospector returned Converter definition of type
- AnnotationIntrospector returned `Class<${deserClass.getName(
- AnnotationIntrospector returned key deserializer definition
- AnnotationIntrospector returned Class ${deserClass.getName()
- Instances of {} should not get visited
AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11).
Data as JSON: /api/errors/426b5dc27755a64a.
Report an issue: GitHub.