{"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/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/DatabindContext.java#L452-L488","documentation":"Thrown by DatabindContext.converterInstance() when the object returned by AnnotationIntrospector is a Class, but that Class does not implement the Converter interface. The @JsonSerialize(converter=X.class) or @JsonDeserialize(converter=X.class) annotation requires X to be a valid Converter implementation.","triggerScenarios":"Annotating a property or type with @JsonSerialize(converter=SomeClass.class) or @JsonDeserialize(converter=SomeClass.class) where SomeClass does not implement tools.jackson.databind.util.Converter. Also triggered by a custom AnnotationIntrospector that returns a Class that is not a Converter subtype.","commonSituations":"Copy-paste errors where a serializer/deserializer class is accidentally used as a converter. Refactoring where a class stopped implementing Converter. Confusion between @JsonSerialize(using=...) and @JsonSerialize(converter=...).","solutions":["Verify the class referenced in converter=... implements tools.jackson.databind.util.Converter.","If you meant to use a custom serializer/deserializer, use the using= or contentUsing= attribute instead of converter=.","Make the referenced class extend StdConverter<IN,OUT> for a zero-boilerplate Converter implementation."],"exampleFix":"// before\n@JsonSerialize(converter = MySerializer.class) // MySerializer extends ValueSerializer, not Converter\n// after\n@JsonSerialize(using = MySerializer.class) // correct attribute for a ValueSerializer","handlingStrategy":"validation","validationCode":"// Verify a converter class before using it in an annotation\nClass<?> converterClass = MyConverter.class;\nif (!Converter.class.isAssignableFrom(converterClass)) {\n    throw new IllegalArgumentException(converterClass + \" does not implement Converter\");\n}","typeGuard":"boolean isValidConverterClass(Class<?> cls) {\n    return cls != null && Converter.class.isAssignableFrom(cls);\n}","tryCatchPattern":null,"preventionTips":["Distinguish @JsonSerialize(using=Serializer.class) from @JsonSerialize(converter=Converter.class).","Have Converter classes extend StdConverter<IN,OUT> for compile-time safety.","Run a quick smoke test (serialize/deserialize one instance) on every annotated type."],"tags":["converter","annotation","serialization","deserialization","misconfiguration"],"backgroundTag":null,"analyzedSha":"87876ca5c0569b4933aec2d30d6225e4b9ba3a43","analyzedAt":"2026-08-11T12:55:24.033Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}