FasterXML/jackson-databind · error · IllegalArgumentException

Duplicate creator property

Error message

Duplicate creator property "%s" (index %s vs %d) for type %s 

What it means

CreatorCollector detects that two parameters of a single property-based creator (constructor or @JsonCreator factory) share the same JSON name. Each creator parameter must map to a unique incoming property name; a duplicate makes deserialization ambiguous so construction is rejected.

Solutions

  1. Give each creator parameter a distinct @JsonProperty value (or let it derive from the parameter name with -parameters compilation).
  2. Annotate injectable parameters with @JacksonInject and ensure their names do not collide (empty-named injectables are skipped).
  3. If merging two parameters, use @JsonUnwrapped or a nested type instead of duplicating the name.
  4. Recompile with -parameters so Jackson can use the actual parameter names and remove redundant @JsonProperty.

Example fix

// before
@JsonCreator
public Foo(@JsonProperty("x") int a, @JsonProperty("x") int b) {} // duplicate

// after
@JsonCreator
public Foo(@JsonProperty("x") int a, @JsonProperty("y") int b) {}
Defensive patterns

Strategy: validation

Validate before calling

Set<String> seen = new HashSet<>();
for (Parameter p : constructor.getParameters()) {
    String n = jsonNameOf(p);
    if (n != null && !n.isEmpty() && !seen.add(n)) {
        throw new IllegalStateException("Duplicate creator property: " + n);
    }
}

Type guard

// no type guard; duplicate-name check is runtime

Try / catch

try { mapper.readValue(json, Foo.class); }
catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Duplicate creator property")) {
        // rename one of the conflicting @JsonProperty parameters
    } else throw e;
}

Prevention

When it happens

Trigger: A @JsonCreator constructor/factory with two parameters both annotated @JsonProperty("sameName"); two parameters whose names deduce to the same string; an injectable (@Inject) and a real parameter colliding after name resolution.

Common situations: Copy-paste of @JsonProperty names across parameters; kotlin/scala compiler-generated constructors that produce duplicate names; mixing @JsonProperty with @JacksonInject where the injectable lacks a distinct name; refactors that rename parameters but not the @JsonProperty values.

Related errors


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

Appendix: source

Thrown at src/main/java/tools/jackson/databind/deser/bean/CreatorCollector.java:182

    public void addPropertyCreator(AnnotatedWithParams creator,
            boolean explicit, SettableBeanProperty[] properties)
    {
        if (verifyNonDup(creator, C_PROPS, explicit)) {
            // Better ensure we have no duplicate names either...
            if (properties.length > 1) {
                HashMap<String, Integer> names = new HashMap<>();
                for (int i = 0, len = properties.length; i < len; ++i) {
                    String name = properties[i].getName();
                    // Need to consider Injectables, which may not have
                    // a name at all, and need to be skipped
                    // (same for possible AnySetter)
                    if (name.isEmpty() && (properties[i].getInjectableValueId() != null)) {
                        continue;
                    }
                    Integer old = names.put(name, Integer.valueOf(i));
                    if (old != null) {
                        throw new IllegalArgumentException("Duplicate creator property \"%s\" (index %s vs %d) for type %s ".formatted(
                                name, old, i, ClassUtil.nameOf(_beanType.getRawClass())));
                    }
                }
            }
            _propertyBasedArgs = properties;
        }
    }

    /*
    /**********************************************************
    /* Accessors
    /**********************************************************
     */

    public boolean hasDefaultCreator() {
        return _creators[C_DEFAULT] != null;
    }

View on GitHub (pinned to 87876ca5c0)