{"record":{"id":"0124beba6690259c","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-key-deserializer-d-0124be","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/DeserializationContextExt.java","lineNumber":283,"sourceCode":"        // First: need to resolve\n        deser.resolve(this);\n        return (ValueDeserializer<Object>) deser;\n    }\n\n    @Override\n    public final KeyDeserializer keyDeserializerInstance(Annotated ann, Object deserDef)\n    {\n        if (deserDef == null) {\n            return null;\n        }\n\n        KeyDeserializer deser;\n\n        if (deserDef instanceof KeyDeserializer keyDeserializer) {\n            deser = keyDeserializer;\n        } else {\n            if (!(deserDef instanceof Class)) {\n                throw new IllegalStateException(\"AnnotationIntrospector returned key deserializer definition of type \"\n                        +deserDef.getClass().getName()\n                        +\"; expected type KeyDeserializer or Class<KeyDeserializer> instead\");\n            }\n            Class<?> deserClass = (Class<?>)deserDef;\n            // there are some known \"no class\" markers to consider too:\n            if (deserClass == KeyDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) {\n                return null;\n            }\n            if (!KeyDeserializer.class.isAssignableFrom(deserClass)) {\n                throw new IllegalStateException(\"AnnotationIntrospector returned Class \"+deserClass.getName()\n                        +\"; expected Class<KeyDeserializer>\");\n            }\n            HandlerInstantiator hi = _config.getHandlerInstantiator();\n            deser = (hi == null) ? null : hi.keyDeserializerInstance(_config, ann, deserClass);\n            if (deser == null) {\n                deser = (KeyDeserializer) ClassUtil.createInstance(deserClass,\n                        _config.canOverrideAccessModifiers());\n            }","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/a50c7d2a1d57234ac4adf70dbd88ac90db6436e4/src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java#L265-L301","documentation":"Thrown by DeserializationContextExt.keyDeserializerInstance when the object returned for a key-deserializer definition is neither a KeyDeserializer instance nor a Class. The introspector must yield one of those two shapes (or null); anything else violates the contract.","triggerScenarios":"A custom AnnotationIntrospector.findKeyDeserializer (or custom @JsonKeyDeserializer handling) returns a non-Class, non-KeyDeserializer object (a String, a Type, a wrapper).","commonSituations":"A custom introspection layer wrapping key-deserializer refs in its own types; an annotation pointing at a name/Type instead of a class; a library upgrade tightening the contract.","solutions":["Audit the introspector's key-deserializer-returning methods: return null, a KeyDeserializer instance, or a Class<? extends KeyDeserializer>.","Resolve any name-based lookup to the actual Class before returning.","Add a contract unit test for the introspector method."],"exampleFix":"// before\npublic Object findKeyDeserializer(Annotated a) {\n    return \"colorKeyDeser\"; // String -> throws\n}\n// after\npublic Object findKeyDeserializer(Annotated a) {\n    return ColorKeyDeserializer.class; // Class<? extends KeyDeserializer>\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isLegalKeyDeserializerDef(Object def) {\n    return def == null\n        || def instanceof KeyDeserializer\n        || (def instanceof Class<?> c && KeyDeserializer.class.isAssignableFrom(c));\n}","tryCatchPattern":null,"preventionTips":["Key-deserializer introspector methods return null|instance|Class.","Keep key and value deserializer resolution distinct in custom introspection code.","Unit-test introspector methods returning key deserializers against the contract."],"tags":["annotation-introspector","key-deserializer","deserialization"],"analyzedSha":"a50c7d2a1d57234ac4adf70dbd88ac90db6436e4","analyzedAt":"2026-08-06T20:31:51.404Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}