{"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/a50c7d2a1d57234ac4adf70dbd88ac90db6436e4/src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java#L267-L303","documentation":"Thrown from BasicDeserializerFactory._valueInstantiatorInstance when the object returned for a ValueInstantiator definition is neither a ValueInstantiator instance nor a Class. IMPORTANT: the message text says 'key deserializer definition' but the code path enforces the VALUE-INSTANTIATOR contract — the wording is a copy-paste bug; the actual fault is in value-instantiator resolution (e.g. via @JsonValueInstantiator or a custom introspector).","triggerScenarios":"A custom AnnotationIntrospector's value-instantiator resolution returns an unexpected type — not a ValueInstantiator, not a Class (e.g. a Type, a TypeReference, a String, or a framework wrapper). Reachable through @JsonCreator/@JsonValueInstantiator handling or a misbehaving HandlerInstantiator.","commonSituations":"A custom introspection framework intercepting value-instantiator resolution and returning its own wrapper type; an annotation that resolves to a Type instead of a Class; a library upgrade where the introspector method signature/contract changed.","solutions":["Audit the introspector: the value-instantiator resolution must return null, a ValueInstantiator instance, or a Class<? extends ValueInstantiator>.","Do NOT trust the 'key deserializer' wording in the message — it is misleading; investigate the ValueInstantiator/@JsonValueInstantiator path first.","If resolving by name, resolve to the raw Class before returning; never return a Type or TypeReference."],"exampleFix":"// before: introspector returns a TypeReference wrapper instead of a Class\npublic Object findValueInstantiator(Annotated a) {\n    return new TypeReference<MyInstantiator>(){}; // not a Class -> throws (with misleading msg)\n}\n// after\npublic Object findValueInstantiator(Annotated a) {\n    return MyInstantiator.class; // raw Class<? extends ValueInstantiator>\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isLegalInstantiatorDef(Object def) {\n    return def == null\n        || def instanceof ValueInstantiator\n        || (def instanceof Class<?> c && ValueInstantiator.class.isAssignableFrom(c));\n}","tryCatchPattern":null,"preventionTips":["The message says 'key deserializer' but the fault is in value-instantiator resolution — check that path first.","Keep introspector handler-returning methods to the documented null|instance|Class contract.","Never return a Type/TypeReference/String for a value-instantiator definition."],"tags":["annotation-introspector","value-instantiator","deserialization","misleading-message"],"analyzedSha":"a50c7d2a1d57234ac4adf70dbd88ac90db6436e4","analyzedAt":"2026-08-06T20:31:51.404Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}