{"record":{"id":"7a686bb919f747d0","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-class-expec","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/cfg/MapperConfigBase.java","lineNumber":748,"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                    +ClassUtil.classNameOf(converterDef)+\"; 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                    +ClassUtil.classNameOf(converterClass)+\"`>; expected `Class<Converter>`\");\n        }\n        HandlerInstantiator hi = getHandlerInstantiator();\n        Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(this, annotated, converterClass);\n        if (conv == null) {\n            conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,\n                    canOverrideAccessModifiers());\n        }\n        return (Converter<Object,Object>) conv;\n    }\n}\n","sourceCodeStart":730,"sourceCodeEnd":760,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/cfg/MapperConfigBase.java#L730-L760","documentation":"Thrown by MapperConfigBase._createConverter when an AnnotationIntrospector returned a Class that does not implement Converter. The converter slot (@JsonDeserialize(converter=...), @JsonSerialize(converter=...), or introspector) must point to a Converter implementation; a class that is not assignable to Converter is rejected at converter-creation time.","triggerScenarios":"Pointing @JsonSerialize/@JsonDeserialize converter to a Class that does not implement tools.jackson.databind.util.Converter, or a custom introspector returning such a class.","commonSituations":"Using a plain Function/lambda class as a converter by mistake; refactoring a class so it no longer implements Converter but keeping the annotation; wrong class referenced in the annotation.","solutions":["Make the referenced class implement Converter<SourceType, TargetType> (implement convert and getInputType/getOutputType).","Point the converter annotation at the correct class that already implements Converter.","If you intended a two-step conversion, wrap it in a proper Converter instead of a Function."],"exampleFix":"// before\n@JsonDeserialize(converter = StringProcessor.class) // not a Converter\npublic class StringProcessor { String apply(String s){...} }\n\n// after\n@JsonDeserialize(converter = StringProcessor.class)\npublic class StringProcessor implements Converter<String, Value> {\n    @Override public Value convert(String s) { ... }\n    @Override public JavaType getInputType(TypeFactory f) { ... }\n    @Override public JavaType getOutputType(TypeFactory f) { ... }\n}","handlingStrategy":"type-guard","validationCode":"Class<?> cls = (Class<?>) introspector.findDeserializationConverter(annotated);\nif (cls != null && !Converter.class.isAssignableFrom(cls)) {\n    throw new IllegalStateException(cls + \" is not a Converter\");\n}","typeGuard":"boolean isConverterClass(Class<?> c) {\n    return c != null && Converter.class.isAssignableFrom(c);\n}","tryCatchPattern":null,"preventionTips":["Ensure every converter class referenced in annotations implements Converter.","Implement getInputType/getOutputType as well as convert.","Run a mapper build with the annotated type in tests to fail fast."],"tags":["jackson","introspector","converter","annotation","config"],"backgroundTag":null,"analyzedSha":"87876ca5c0569b4933aec2d30d6225e4b9ba3a43","analyzedAt":"2026-08-11T12:55:24.033Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}