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
- Keep exactly one @JsonCreator-annotated single-String factory method on the key class and remove the annotation from the others.
- If two factories are genuinely needed, mark the undesired ones with @JsonCreator(mode=Mode.DISABLED) or @JsonIgnore.
- For valueOf/fromStyle factories, leave them implicit (no annotation) so Jackson's precedence (valueOf then fromString) applies without ambiguity.
- 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
- Keep at most one @JsonCreator on single-String factory methods of any class used as a Map key.
- Prefer implicit valueOf/fromString (no annotation) so Jackson applies deterministic precedence.
- Add a startup test that fails the build if a key type gains a second annotated single-String creator.
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
- Argument `allowedSchemes` must not be null
- Cannot deserialize Singleton container from "+size+" entries
- Missing generic type information for "+type
- Trying to resolve a forward reference with id [" + id + "]…
- Trying to resolve a forward reference with id
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)