{"record":{"id":"786c30b67d4a1687","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-key-deserializer-d","errorCode":null,"errorMessage":"AnnotationIntrospector returned key deserializer definition of type {}; expected type KeyDeserializer or Class<KeyDeserializer> instead","messagePattern":"AnnotationIntrospector returned key deserializer definition of type (.+?); expected type KeyDeserializer or Class<KeyDeserializer> instead","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java","lineNumber":285,"sourceCode":"        }\n\n        return creators.constructValueInstantiator(ctxt);\n    }\n\n    public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config,\n            Annotated annotated, Object instDef)\n    {\n        if (instDef == null) {\n            return null;\n        }\n\n        ValueInstantiator inst;\n\n        if (instDef instanceof ValueInstantiator valueInstantiator) {\n            return valueInstantiator;\n        }\n        if (!(instDef instanceof Class)) {\n            throw new IllegalStateException(\"AnnotationIntrospector returned key deserializer definition of type \"\n                    +instDef.getClass().getName()\n                    +\"; expected type KeyDeserializer or Class<KeyDeserializer> instead\");\n        }\n        Class<?> instClass = (Class<?>)instDef;\n        if (ClassUtil.isBogusClass(instClass)) {\n            return null;\n        }\n        if (!ValueInstantiator.class.isAssignableFrom(instClass)) {\n            throw new IllegalStateException(\"AnnotationIntrospector returned Class \"+instClass.getName()\n                    +\"; expected Class<ValueInstantiator>\");\n        }\n        HandlerInstantiator hi = config.getHandlerInstantiator();\n        if (hi != null) {\n            inst = hi.valueInstantiatorInstance(config, annotated, instClass);\n            if (inst != null) {\n                return inst;\n            }\n        }","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java#L267-L303","documentation":"Thrown by BasicDeserializerFactory._valueInstantiatorInstance when an AnnotationIntrospector returned a value-instantiator definition whose runtime type is neither a ValueInstantiator instance nor a Class. NOTE: the message text in the source says 'key deserializer definition' / 'KeyDeserializer', but the code path actually handles @JsonValueInstantiator / ValueInstantiator resolution -- the wording is misleading; the trigger is a malformed ValueInstantiator definition returned by a custom introspector.","triggerScenarios":"A custom AnnotationIntrospector returning an arbitrary non-Class object from the value-instantiator lookup (used for @JsonValueInstantiator), or annotation data that resolves to something other than a ValueInstantiator or Class.","commonSituations":"Custom introspector implementation returning the wrong object type for the value-instantiator slot; a plugin/framework remapping annotations incorrectly; copying an introspector pattern from a key-deserializer hook into the value-instantiator hook.","solutions":["Fix the custom AnnotationIntrospector to return a ValueInstantiator instance or a Class<? extends ValueInstantiator> for the value-instantiator slot (or null).","Check @JsonValueInstantiator on the target type points to a Class<ValueInstantiator> and is not being rewritten by a mixin/introspector.","If you do not need a custom instantiator, remove the annotation/introspector override."],"exampleFix":"// before: introspector returns a String for the instantiator slot\n@Override\npublic Object findValueInstantiator(Annotated a) {\n    return a.getName();\n}\n\n// after\n@Override\npublic Object findValueInstantiator(Annotated a) {\n    return MyValueInstantiator.class;\n}","handlingStrategy":"type-guard","validationCode":"Object def = introspector.findValueInstantiator(annotated);\nif (def != null && !(def instanceof ValueInstantiator) && !(def instanceof Class)) {\n    throw new IllegalStateException(\"Bad value-instantiator definition: \" + def.getClass());\n}","typeGuard":"boolean isInstantiatorDef(Object d) {\n    return d == null || d instanceof ValueInstantiator || d instanceof Class;\n}","tryCatchPattern":null,"preventionTips":["Custom introspectors must return ValueInstantiator, Class<? extends ValueInstantiator>, or null from the instantiator hook.","Note the source message text says 'key deserializer' but the trigger is the value-instantiator slot -- do not be misled.","Unit-test introspector return values for each annotated type."],"tags":["jackson","introspector","value-instantiator","annotation","config"],"backgroundTag":null,"analyzedSha":"87876ca5c0569b4933aec2d30d6225e4b9ba3a43","analyzedAt":"2026-08-11T12:55:24.033Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}