{"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/a50c7d2a1d57234ac4adf70dbd88ac90db6436e4/src/main/java/tools/jackson/databind/cfg/MapperConfigBase.java#L721-L757","documentation":"Thrown by MapperConfigBase._createConverter when resolving a converter definition: the AnnotationIntrospector returned an object that is neither a Converter instance nor a Class. The introspector contract requires one of those two shapes (or null), so any other type is a bug in the introspector or a misbehaving annotation mapping.","triggerScenarios":"A custom AnnotationIntrospector overriding findSerializationConverter/findDeserializationConverter (or equivalent) returns a String, a Type, a TypeReference, or some framework wrapper object instead of a Converter or Class<Converter>.","commonSituations":"A custom framework introspection layer that maps its own annotations to Jackson handlers but wraps refs in its own types; a buggy mixin/plugin intercepting converter resolution; returning a fully-qualified class name String instead of a Class.","solutions":["Audit the custom AnnotationIntrospector: every converter-returning method must yield null, a Converter<?,?> instance, or a Class<? extends Converter>.","If resolving by name, look up the actual Class (Class.forName / registry) and return the raw Class, not a wrapper or String.","Add a contract unit test asserting the introspector's converter method returns one of the legal shapes on every code path."],"exampleFix":"// before\npublic class MyIntrospector extends AnnotationIntrospector {\n    @Override public Object findDeserializationConverter(Annotated a) {\n        return \"com.example.MyConverter\"; // String -> throws\n    }\n}\n// after\npublic class MyIntrospector extends AnnotationIntrospector {\n    @Override public Object findDeserializationConverter(Annotated a) {\n        return MyConverter.class; // Class<? extends Converter>\n    }\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isLegalConverterDef(Object def) {\n    return def == null\n        || def instanceof Converter<?,?>\n        || (def instanceof Class<?> c && Converter.class.isAssignableFrom(c));\n}","tryCatchPattern":null,"preventionTips":["Custom introspector methods that resolve handlers must return instances or Class objects, never strings or wrapper types.","Unit-test introspector return values against the documented null|instance|Class contract.","Keep a single helper that validates every handler-def shape and route all introspector returns through it."],"tags":["annotation-introspector","converter","serialization"],"analyzedSha":"a50c7d2a1d57234ac4adf70dbd88ac90db6436e4","analyzedAt":"2026-08-06T20:31:51.404Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}