{"record":{"id":"011c1d6476373aac","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-deserializer-defin","errorCode":null,"errorMessage":"AnnotationIntrospector returned deserializer definition of type {}; expected type `ValueDeserializer` or `Class<ValueDeserializer>` instead","messagePattern":"AnnotationIntrospector returned deserializer definition of type (.+?); expected type `ValueDeserializer` or `Class<ValueDeserializer>` instead","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java","lineNumber":246,"sourceCode":"    /**********************************************************************\n     */\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public ValueDeserializer<Object> deserializerInstance(Annotated ann, Object deserDef)\n    {\n        if (deserDef == null) {\n            return null;\n        }\n        ValueDeserializer<?> deser;\n\n        if (deserDef instanceof ValueDeserializer valueDeserializer) {\n            deser = valueDeserializer;\n        } else {\n            // Alas, there's no way to force return type of \"either class\n            // X or Y\" -- need to throw an exception after the fact\n            if (!(deserDef instanceof Class)) {\n                throw new IllegalStateException(\"AnnotationIntrospector returned deserializer definition of type \"\n                        +deserDef.getClass().getName()\n                        +\"; expected type `ValueDeserializer` or `Class<ValueDeserializer>` instead\");\n            }\n            Class<?> deserClass = (Class<?>)deserDef;\n            // there are some known \"no class\" markers to consider too:\n            if (deserClass == ValueDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) {\n                return null;\n            }\n            if (!ValueDeserializer.class.isAssignableFrom(deserClass)) {\n                throw new IllegalStateException(\"AnnotationIntrospector returned `Class<\"+deserClass.getName()+\">`; expected `Class<ValueDeserializer>`\");\n            }\n            HandlerInstantiator hi = _config.getHandlerInstantiator();\n            deser = (hi == null) ? null : hi.deserializerInstance(_config, ann, deserClass);\n            if (deser == null) {\n                deser = (ValueDeserializer<?>) ClassUtil.createInstance(deserClass,\n                        _config.canOverrideAccessModifiers());\n            }\n        }","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/a50c7d2a1d57234ac4adf70dbd88ac90db6436e4/src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java#L228-L264","documentation":"Thrown by DeserializationContextExt.deserializerInstance when the object returned for a deserializer definition is neither a ValueDeserializer instance nor a Class. The introspector (or @JsonDeserialize(using=...)) must yield one of those two shapes (or null); anything else violates the contract.","triggerScenarios":"A custom AnnotationIntrospector.findDeserializer (or equivalent) returns a String, a Type, a Supplier, a bean-name, or another wrapper instead of a ValueDeserializer instance or Class<ValueDeserializer>.","commonSituations":"A custom framework introspection layer that wraps handler refs in its own types; a library upgrade where the introspector contract tightened; an annotation pointing at a bean name instead of a class.","solutions":["Audit the introspector's deserializer-returning methods: return null, a ValueDeserializer instance, or a Class<? extends ValueDeserializer>.","If resolving handlers by name, resolve the actual Class first and return that.","Add a contract unit test covering all code paths of the introspector method."],"exampleFix":"// before\npublic Object findDeserializer(Annotated a) {\n    return \"myCustomDeserializer\"; // String -> throws\n}\n// after\npublic Object findDeserializer(Annotated a) {\n    return MyCustomDeserializer.class; // Class<? extends ValueDeserializer>\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isLegalDeserializerDef(Object def) {\n    return def == null\n        || def instanceof ValueDeserializer\n        || (def instanceof Class<?> c && ValueDeserializer.class.isAssignableFrom(c));\n}","tryCatchPattern":null,"preventionTips":["Introspector handler methods return null|instance|Class — never names, strings, or wrappers.","If resolving handlers by name, resolve to the Class before returning.","Centralize handler-def validation in a shared helper used by all introspector overrides."],"tags":["annotation-introspector","deserializer","deserialization"],"analyzedSha":"a50c7d2a1d57234ac4adf70dbd88ac90db6436e4","analyzedAt":"2026-08-06T20:31:51.404Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}