{"record":{"id":"371d132f044e1f94","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-converter-defi","errorCode":null,"errorMessage":"`AnnotationIntrospector` returned `Converter` definition of type {}; expected type `Converter` or `Class<Converter>` instead","messagePattern":"`AnnotationIntrospector` returned `Converter` definition of type (.+?); expected type `Converter` or `Class<Converter>` instead","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/tools/jackson/databind/cfg/MapperConfigBase.java","lineNumber":739,"sourceCode":"\n    /*\n    /**********************************************************************\n    /* Helper methods for implementations\n    /**********************************************************************\n     */\n\n    @SuppressWarnings(\"unchecked\")\n    protected Converter<Object,Object> _createConverter(Annotated annotated,\n            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;","sourceCodeStart":721,"sourceCodeEnd":757,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/cfg/MapperConfigBase.java#L721-L757","documentation":"Thrown by MapperConfigBase._createConverter when an AnnotationIntrospector returned a Converter definition whose runtime type is neither a Converter instance nor a Class. Jackson expects @JsonSerialize(converter=...)/@JsonDeserialize(converter=...) (or the introspector equivalents) to resolve to a Converter or a Class<Converter>; anything else is treated as an introspector bug.","triggerScenarios":"A custom AnnotationIntrospector's findDeserializationConverter / findSerializationConverter returns an arbitrary object (e.g. a String, a Type, a Method) instead of a Converter or Class. Also possible via a faulty mixin or annotation-based plugin.","commonSituations":"Writing a custom introspector and returning the wrong field by mistake; a framework that wraps annotations and leaks an internal type; upgrading Jackson where the introspector contract was tightened.","solutions":["Fix the custom AnnotationIntrospector to return a Converter instance or a Class<? extends Converter> (or Converter.None marker, or null).","If using @JsonDeserialize(converter=X.class)/@JsonSerialize(converter=X.class), ensure the annotation value is a Converter class and not being remapped by a mixin/introspector.","Remove or correct the introspector override that is rewriting converter definitions."],"exampleFix":"// before\n@Override\npublic Object findDeserializationConverter(Annotated a) {\n    return a.getName(); // wrong type: String\n}\n\n// after\n@Override\npublic Object findDeserializationConverter(Annotated a) {\n    return MyConverter.class; // Class<? extends Converter>\n}","handlingStrategy":"type-guard","validationCode":"Object def = introspector.findDeserializationConverter(annotated);\nif (def != null && !(def instanceof Converter) && !(def instanceof Class)) {\n    throw new IllegalStateException(\"Bad converter definition: \" + def.getClass());\n}","typeGuard":"boolean isConverterDef(Object d) {\n    return d == null || d instanceof Converter<?,?> || d instanceof Class;\n}","tryCatchPattern":"try {\n    mapper.readValue(json, Target.class);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"Converter definition of type\")) {\n        // fix the introspector to return Converter or Class\n    }\n}","preventionTips":["Custom introspectors must return Converter, Class<? extends Converter>, Converter.None, or null from converter hooks.","Add unit tests for introspector return types on every annotated element.","Avoid remapping annotations to internal descriptor objects."],"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"}