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 messages

View on GitHub (pinned to 87876ca5c0)

Solutions

  1. Return a Class<? extends ValueSerializer> or null from the introspector method named in the message.
  2. If you must supply an instance, register it directly via a Module/SerializerFactory or ObjectMapper API rather than through the introspector.
  3. 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

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


AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11). Data as JSON: /api/errors/426b5dc27755a64a. Report an issue: GitHub.