FasterXML/jackson-databind · error · IllegalArgumentException

Multiple suitable annotated Creator factory methods to be…

Error message

Multiple suitable annotated Creator factory methods to be used as the Key deserializer for type "+ClassUtil.nameOf(rawKeyType)

What it means

When a class is used as a Map key, Jackson builds a key deserializer from a delegating Creator (a single-String-arg constructor or static factory method). JDKKeyDeserializers._findExplicitStringFactoryMethod throws IllegalArgumentException if more than one explicit (@JsonCreator-annotated, non-PROPERTIES) factory method taking one String is found, because the choice would be ambiguous. The reported type is the declaring class of the second candidate.

Solutions

  1. Keep exactly one @JsonCreator-annotated single-String factory method on the key class and remove the annotation from the others.
  2. If two factories are genuinely needed, mark the undesired ones with @JsonCreator(mode=Mode.DISABLED) or @JsonIgnore.
  3. For valueOf/fromStyle factories, leave them implicit (no annotation) so Jackson's precedence (valueOf then fromString) applies without ambiguity.
  4. Register a custom KeyDeserializer for the type to bypass factory-based resolution entirely.

Example fix

// before
public final class MyKey {
    @JsonCreator public static MyKey valueOf(String s) { ... }
    @JsonCreator public static MyKey fromString(String s) { ... }
}

// after
public final class MyKey {
    @JsonCreator public static MyKey valueOf(String s) { ... }
    public static MyKey fromString(String s) { ... } // implicit only
}
Defensive patterns

Strategy: validation

Validate before calling

// At app startup, introspect key types used as Map keys
BeanDescription bd = mapper.getSerializationConfig()
    .introspect(mapper.constructType(MyKey.class));
long explicitSingleStringFactories = bd.getFactoryMethodsWithMode().stream()
    .filter(m -> m.metadata != null
        && m.annotated.getParameterCount() == 1
        && m.annotated.getRawParameterType(0) == String.class)
    .count();
if (explicitSingleStringFactories > 1) {
    throw new IllegalStateException("MyKey has ambiguous key creators");
}

Prevention

When it happens

Trigger: A Map key type declares two or more @JsonCreator static methods that each accept a single String (e.g. valueOf(String) and fromString(String) both annotated); a Map<MyKey, V> where MyKey has multiple annotated factory overloads that collapse to the same single-String signature.

Common situations: Adding a fromString factory to a class that already has an annotated valueOf; copying annotations during a refactor; libraries (like records with custom factories) that auto-generate multiple candidate creators; upgrading Jackson so that previously-implicit candidates now become explicit and conflict.

Related errors


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

Appendix: source

Thrown at src/main/java/tools/jackson/databind/deser/jdk/JDKKeyDeserializers.java:201

        }
        return null;
    }

    private static AnnotatedMethod _findExplicitStringFactoryMethod(DeserializationContext ctxt,
            List<AnnotatedAndMetadata<AnnotatedMethod, JsonCreator.Mode>> candidates)
        throws JacksonException
    {
        AnnotatedMethod match = null;
        for (AnnotatedAndMetadata<AnnotatedMethod, JsonCreator.Mode> entry : candidates) {
            // Note: caller has filtered out invalid candidates; all we need to check are dups
            if (entry.metadata != null) {
                if (match == null) {
                    match = entry.annotated;
                } else {
                    // 15-Jun-2021, tatu: Not optimal type or information, but has to do for now
                    //    since we do not get DeserializationContext
                    Class<?> rawKeyType = entry.annotated.getDeclaringClass();
                    throw new IllegalArgumentException(
"Multiple suitable annotated Creator factory methods to be used as the Key deserializer for type "
                            +ClassUtil.nameOf(rawKeyType));
                }
            }
        }
        return match;
    }

    /*
    /**********************************************************************
    /* KeyDeserializers implementation
    /**********************************************************************
     */

    @Override
    public KeyDeserializer findKeyDeserializer(JavaType type,
            DeserializationConfig config, BeanDescription.Supplier beanDescRef)
    {

View on GitHub (pinned to 87876ca5c0)