{"record":{"id":"7bd318a79ba120a8","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-class-expected-7bd318","errorCode":null,"errorMessage":"AnnotationIntrospector returned Class {}; expected Class<KeyDeserializer>","messagePattern":"AnnotationIntrospector returned Class (.+?); expected Class<KeyDeserializer>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java","lineNumber":293,"sourceCode":"        }\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            }\n        }\n        // First: need to resolve\n        deser.resolve(this);\n        return deser;\n    }\n\n    /*\n    /**********************************************************************\n    /* Extended API, read methods\n    /**********************************************************************","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/FasterXML/jackson-databind/blob/a50c7d2a1d57234ac4adf70dbd88ac90db6436e4/src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java#L275-L311","documentation":"Thrown by DeserializationContextExt.keyDeserializerInstance when the introspector returns a Class for the key deserializer, but that Class does not extend KeyDeserializer. Only KeyDeserializer subclasses can be instantiated to deserialize map keys.","triggerScenarios":"@JsonKeyDeserializer(MyClass.class) where MyClass does not extend KeyDeserializer, or a custom introspector returning a non-KeyDeserializer Class.","commonSituations":"Pointing @JsonKeyDeserializer at a regular value deserializer or utility class; a refactor that dropped the extends clause; confusing value and key deserializer types.","solutions":["Make the referenced class extend KeyDeserializer and implement deserializeKey().","If you meant a value deserializer, use @JsonDeserialize(using=...) instead.","Verify the class hierarchy before wiring."],"exampleFix":"// before\n@JsonKeyDeserializer(ColorParser.class) // not a KeyDeserializer\nclass ColorParser { Color parse(String s){ ... } }\n// after\nclass ColorKeyDeserializer extends KeyDeserializer {\n    @Override public Object deserializeKey(String key, DeserializationContext ctxt){ ... }\n}\n@JsonKeyDeserializer(ColorKeyDeserializer.class)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isKeyDeserializerClass(Class<?> c) {\n    return c != null && KeyDeserializer.class.isAssignableFrom(c);\n}","tryCatchPattern":null,"preventionTips":["Verify @JsonKeyDeserializer targets extend KeyDeserializer.","Don't confuse key deserializers (KeyDeserializer) with value deserializers (ValueDeserializer) — different base class.","Add a smoke test that builds a mapper for each type using @JsonKeyDeserializer."],"tags":["annotation-introspector","key-deserializer","type-mismatch"],"analyzedSha":"a50c7d2a1d57234ac4adf70dbd88ac90db6436e4","analyzedAt":"2026-08-06T20:31:51.404Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}