FasterXML/jackson-databind · error · IllegalStateException

AnnotationIntrospector returned Class ${deserClass.getName()

Error message

AnnotationIntrospector returned Class ${deserClass.getName()}; expected Class<KeyDeserializer>

What it means

Thrown when keyDeserializerInstance receives a Class as the key-deserializer definition, but that Class does not extend KeyDeserializer. This is the class-typed sibling of error 41: the value is a Class (so it passed the first check) but it is the wrong kind of class.

Source

Thrown at src/main/java/tools/jackson/databind/deser/DeserializationContextExt.java:293

        }

        KeyDeserializer deser;

        if (deserDef instanceof KeyDeserializer keyDeserializer) {
            deser = keyDeserializer;
        } else {
            if (!(deserDef instanceof Class)) {
                throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
                        +deserDef.getClass().getName()
                        +"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
            }
            Class<?> deserClass = (Class<?>)deserDef;
            // there are some known "no class" markers to consider too:
            if (deserClass == KeyDeserializer.None.class || ClassUtil.isBogusClass(deserClass)) {
                return null;
            }
            if (!KeyDeserializer.class.isAssignableFrom(deserClass)) {
                throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName()
                        +"; expected Class<KeyDeserializer>");
            }
            HandlerInstantiator hi = _config.getHandlerInstantiator();
            deser = (hi == null) ? null : hi.keyDeserializerInstance(_config, ann, deserClass);
            if (deser == null) {
                deser = (KeyDeserializer) ClassUtil.createInstance(deserClass,
                        _config.canOverrideAccessModifiers());
            }
        }
        // First: need to resolve
        deser.resolve(this);
        return deser;
    }

    /*
    /**********************************************************************
    /* Extended API, read methods
    /**********************************************************************

View on GitHub (pinned to 87876ca5c0)

Solutions

  1. Ensure the referenced class extends tools.jackson.databind.KeyDeserializer and overrides deserializeKey(String, DeserializationContext).
  2. If you intended a value deserializer, register it via @JsonDeserialize(contentUsing = ...) instead of keyUsing.
  3. Return KeyDeserializer.None.class or null to disable the custom key deserializer.

Example fix

// before
@JsonDeserialize(keyUsing = MyValueDeserializer.class)
Map<String,V> map; // MyValueDeserializer extends ValueDeserializer, not KeyDeserializer

// after
@JsonDeserialize(keyUsing = MyKeyDeserializer.class)
Map<String,V> map; // MyKeyDeserializer extends KeyDeserializer
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = (Class<?>) def;
if (c != null && c != KeyDeserializer.None.class
        && !KeyDeserializer.class.isAssignableFrom(c)) {
    throw new IllegalStateException("Not a KeyDeserializer: " + c);
}

Type guard

static boolean isKeyDeserializerClass(Class<?> c) {
    return c != null && c != KeyDeserializer.None.class
        && KeyDeserializer.class.isAssignableFrom(c);
}

Try / catch

try { mapper.readValue(json, mapType); }
catch (IllegalStateException e) {
    if (e.getMessage().contains("expected Class<KeyDeserializer>")) {
        // referenced class must extend KeyDeserializer
    } else throw e;
}

Prevention

When it happens

Trigger: @JsonDeserialize(keyUsing = SomeClass.class) where SomeClass does not extend KeyDeserializer; an AnnotationIntrospector returning Class<ValueDeserializer> or any unrelated Class for a map key deserializer.

Common situations: Reusing a value deserializer class for map keys; forgetting that key deserializers must extend KeyDeserializer specifically; refactor that changed a class's hierarchy after it was referenced in annotations.

Related errors


AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11). Data as JSON: /api/errors/8b7980714a41aa88. Report an issue: GitHub.