{"record":{"id":"e77c525839480c12","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-class-expected","errorCode":null,"errorMessage":"AnnotationIntrospector returned Class {}; expected Class<Converter>","messagePattern":"AnnotationIntrospector returned Class (.+?); expected Class<Converter>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/tools/jackson/databind/DatabindContext.java","lineNumber":470,"sourceCode":"            Object converterDef)\n    {\n        if (converterDef == null) {\n            return null;\n        }\n        if (converterDef instanceof Converter<?,?>) {\n            return (Converter<Object,Object>) converterDef;\n        }\n        if (!(converterDef instanceof Class)) {\n            throw new IllegalStateException(\"AnnotationIntrospector returned Converter definition of type \"\n                    +converterDef.getClass().getName()+\"; expected type Converter or Class<Converter> instead\");\n        }\n        Class<?> converterClass = (Class<?>)converterDef;\n        // there are some known \"no class\" markers to consider too:\n        if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) {\n            return null;\n        }\n        if (!Converter.class.isAssignableFrom(converterClass)) {\n            throw new IllegalStateException(\"AnnotationIntrospector returned Class \"\n                    +converterClass.getName()+\"; expected Class<Converter>\");\n        }\n        final MapperConfig<?> config = getConfig();\n        HandlerInstantiator hi = config.getHandlerInstantiator();\n        Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(config, annotated, converterClass);\n        if (conv == null) {\n            conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,\n                    config.canOverrideAccessModifiers());\n        }\n        return (Converter<Object,Object>) conv;\n    }\n\n    /*\n    /**********************************************************************\n    /* Misc config access\n    /**********************************************************************\n     */\n","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/a50c7d2a1d57234ac4adf70dbd88ac90db6436e4/src/main/java/tools/jackson/databind/DatabindContext.java#L452-L488","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\n@JsonSerialize(converter = MySerializer.class) // MySerializer extends ValueSerializer, NOT Converter\npublic Value getValue() { ... }\n// after\n@JsonSerialize(using = MySerializer.class)\npublic Value getValue() { ... }","handlingStrategy":"type-guard","validationCode":"// Before returning a converter Class, verify it implements Converter\nstatic Class<?> checkedConverterClass(Class<?> c) {\n    if (c != null && !Converter.class.isAssignableFrom(c)) {\n        throw new IllegalStateException(c.getName() + \" is not a Converter\");\n    }\n    return c;\n}","typeGuard":"boolean isConverterClass(Class<?> c) {\n    return c != null && Converter.class.isAssignableFrom(c);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["introspection","converter","annotation-introspector","configuration"],"analyzedSha":"a50c7d2a1d57234ac4adf70dbd88ac90db6436e4","analyzedAt":"2026-08-06T20:31:51.404Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}