baomidou/mybatis-plus · error · IllegalArgumentException

%s already contains value for %s

Error message

%s already contains value for %s

What it means

Thrown by MybatisConfiguration.StrictMap.put when a duplicate key is registered in a strict map (mapper statements, result maps, sql fragments, parameter maps). MyBatis-Plus copies MyBatis's StrictMap semantics: each fully-qualified id must be unique, so re-registering an id is treated as a configuration defect rather than silently overwriting. The extra detail comes from an optional conflictMessageProducer that can show both conflicting values.

Source

Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java:454

        /**
         * Assign a function for producing a conflict error message when contains value with the same key.
         * <p>
         * function arguments are 1st is saved value and 2nd is target value.
         *
         * @param conflictMessageProducer A function for producing a conflict error message
         * @return a conflict error message
         * @since 3.5.0
         */
        public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
            this.conflictMessageProducer = conflictMessageProducer;
            return this;
        }

        @Override
        @SuppressWarnings("unchecked")
        public V put(String key, V value) {
            if (containsKey(key)) {
                throw new IllegalArgumentException(name + " already contains value for " + key
                    + (conflictMessageProducer == null ? StringPool.EMPTY : conflictMessageProducer.apply(super.get(key), value)));
            }
            if (useGeneratedShortKey) {
                if (key.contains(StringPool.DOT)) {
                    final String shortKey = getShortName(key);
                    if (super.get(shortKey) == null) {
                        super.put(shortKey, value);
                    } else {
                        super.put(shortKey, (V) AMBIGUITY_INSTANCE);
                    }
                }
            }
            return super.put(key, value);
        }

        @Override
        public boolean containsKey(Object key) {
            if (key == null) {

View on GitHub (pinned to bf67d90747)

Solutions

  1. Search all mapper XML namespaces and annotated mapper interfaces for the duplicated statement id shown in the message and rename one of them.
  2. If the id exists both as an annotation (@Select etc.) and in XML for the same mapper method, remove one of the two definitions.
  3. Check for the same XML file being loaded twice (duplicate <mapper resource=...> entries in mybatis-config.xml, or the same jar included twice in the build).
  4. If intentional overriding is required, register the overriding mapper in a different namespace instead of reusing the same id.

Example fix

<!-- before: two files both declare id selectById under namespace com.example.UserMapper -->
<mapper namespace="com.example.UserMapper">
  <select id="selectById">...</select>
</mapper>

<!-- after: give the second statement a distinct id/namespace -->
<mapper namespace="com.example.UserMapperXml">
  <select id="selectByIdLegacy">...</select>
</mapper>
Defensive patterns

Strategy: validation

Validate before calling

// before building the SqlSessionFactory, assert no duplicate statement ids across mappers
Set<String> ids = new HashSet<>();
for (MapperMethod-ish m : allMapperMethods) { /* illustrative */ }
// practical check: after factory build, walk mapped statements
org.apache.ibatis.session.Configuration cfg = factory.getConfiguration();
Set<String> seen = new HashSet<>();
for (Object ms : new java.util.ArrayList<>(cfg.getMappedStatements())) {
    if (ms instanceof org.apache.ibatis.mapping.MappedStatement) {
        String id = ((org.apache.ibatis.mapping.MappedStatement) ms).getId();
        if (!seen.add(id)) throw new IllegalStateException("Duplicate statement id: " + id);
    }
}

Try / catch

catch (IllegalArgumentException e) when message starts with the StrictMap name + " already contains value for" — extract the key from the message and report the conflicting mapper files; fail startup, do not retry.

Prevention

When it happens

Trigger: Two XML mapper files or annotation methods defining the same statement id (e.g. 'com.example.UserMapper.selectById' in both an XML file and a @Select annotated method), or the same mapper XML loaded twice via different <mapper> entries; also registering two result maps or sql fragments with the same namespace-qualified id.

Common situations: Copying a mapper XML and forgetting to rename the namespace; mixing annotation-based statements with an XML file that has the same id; classpath duplication where the same mapper XML is packaged twice; switching a project to mybatis-plus while old MyBatis mapper XML files remain on the classpath.

Related errors


AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14). Data as JSON: /api/errors/2a069f03138cb37b. Report an issue: GitHub.