{"record":{"id":"4e917b1e01ae7326","repo":"FasterXML/jackson-databind","slug":"annotationintrospector-returned-key-deserializer-d-4e917b","errorCode":null,"errorMessage":"AnnotationIntrospector returned key deserializer definition of type ${deserDef.getClass().getName()}; 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/87876ca5c0569b4933aec2d30d6225e4b9ba3a43/src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java#L265-L301","documentation":"Thrown by keyDeserializerInstance when the object returned as a key-deserializer definition is neither a KeyDeserializer instance nor a Class. Jackson only accepts those two shapes for a key deserializer definition; anything else (a String, an arbitrary bean, a TypeReference) is rejected.","triggerScenarios":"An AnnotationIntrospector.findKeyDeserializer() override returning an object that is not a KeyDeserializer and not a Class; a custom module/key-deserializer registration passing the wrong object type; using @JsonKeyDeserializer with a value that is neither a class nor an instance.","commonSituations":"Returning the value deserializer instance where the key deserializer was expected; returning a Class<ValueDeserializer> instead of Class<KeyDeserializer>; a buggy custom introspector returning the raw annotation or a configuration object.","solutions":["Return null, KeyDeserializer.None.class, a KeyDeserializer instance, or a Class<? extends KeyDeserializer> from findKeyDeserializer().","If you need a class, ensure it is a Class object (not an instance) and that it extends KeyDeserializer (see error 42).","Double-check that the introspector method override is for keys, not values."],"exampleFix":"// before\n@Override\npublic Object findKeyDeserializer(Annotated a) {\n    return new MyValueDeserializer(); // wrong type\n}\n\n// after\n@Override\npublic Object findKeyDeserializer(Annotated a) {\n    return new MyKeyDeserializer(); // extends KeyDeserializer\n}","handlingStrategy":"type-guard","validationCode":"Object def = introspector.findKeyDeserializer(annotated);\nif (def != null && !(def instanceof KeyDeserializer) && !(def instanceof Class)) {\n    throw new IllegalStateException(\"Bad key deserializer def: \" + def.getClass());\n}","typeGuard":"static boolean isAcceptableKeyDeserDef(Object def) {\n    return def == null || def instanceof KeyDeserializer || def instanceof Class;\n}","tryCatchPattern":"try { mapper.readValue(json, type); }\ncatch (IllegalStateException e) {\n    if (e.getMessage().contains(\"expected type KeyDeserializer or Class<KeyDeserializer>\")) {\n        // introspector returned wrong shape; fix findKeyDeserializer\n    } else throw e;\n}","preventionTips":["From custom introspectors, return only null, KeyDeserializer.None.class, a KeyDeserializer, or a Class<? extends KeyDeserializer>.","Keep key- and value-deserializer introspection methods separate; do not share return values."],"tags":["deserialization","key-deserializer","annotation-introspector","configuration"],"backgroundTag":null,"analyzedSha":"87876ca5c0569b4933aec2d30d6225e4b9ba3a43","analyzedAt":"2026-08-11T12:55:24.033Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}