{"record":{"id":"9e64c1d65c888938","repo":"apache/incubator-seata","slug":"err-config","errorCode":"ERR_CONFIG","errorMessage":"Duplicate placeholders exist '{}' in bundle.","messagePattern":"Duplicate placeholders exist '(.+?)' in bundle\\.","errorType":"error_code","errorClass":"SeataRuntimeException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/org/apache/seata/common/exception/ResourceBundleUtil.java","lineNumber":113,"sourceCode":"        String value = StringUtils.EMPTY;\n        if (remoteBundle != null && remoteBundle.containsKey(key)) {\n            value = remoteBundle.getString(key);\n        }\n        if (StringUtils.isEmpty(value)) {\n            value = localBundle.getString(key);\n        }\n        return value;\n    }\n\n    protected String parseStringValue(String strVal, Set<String> visitedPlaceholders) {\n        StringBuffer buf = new StringBuffer(strVal);\n        int startIndex = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);\n        while (startIndex != -1) {\n            int endIndex = findPlaceholderEndIndex(buf, startIndex);\n            if (endIndex != -1) {\n                String placeholder = buf.substring(startIndex + DEFAULT_PLACEHOLDER_PREFIX.length(), endIndex);\n                if (!visitedPlaceholders.add(placeholder)) {\n                    throw new SeataRuntimeException(\n                            ErrorCode.ERR_CONFIG, \"Duplicate placeholders exist '\" + placeholder + \"' in bundle.\");\n                }\n                placeholder = parseStringValue(placeholder, visitedPlaceholders);\n                try {\n                    String propVal = resolvePlaceholder(placeholder);\n                    if (propVal != null) {\n                        propVal = parseStringValue(propVal, visitedPlaceholders);\n                        buf.replace(startIndex, endIndex + DEFAULT_PLACEHOLDER_SUFFIX.length(), propVal);\n                        startIndex = buf.indexOf(DEFAULT_PLACEHOLDER_PREFIX, startIndex + propVal.length());\n                    } else {\n                        throw new SeataRuntimeException(\n                                ErrorCode.ERR_CONFIG, \"Could not resolve placeholder '\" + placeholder + \"'\");\n                    }\n                } catch (Exception ex) {\n                    throw new SeataRuntimeException(\n                            ErrorCode.ERR_CONFIG, \"Could not resolve placeholder '\" + placeholder + \"'\");\n                }\n                visitedPlaceholders.remove(placeholder);","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/incubator-seata/blob/e01f97c6db397165050caa6764020410c2c8199a/common/src/main/java/org/apache/seata/common/exception/ResourceBundleUtil.java#L95-L131","documentation":"ResourceBundleUtil.parseStringValue resolves nested ${...} placeholders in seata resource bundles. It tracks each placeholder name in a visitedPlaceholders set; if the same placeholder name appears again on the current resolution path (visitedPlaceholders.add returns false), it throws SeataRuntimeException(ERR_CONFIG) declaring a duplicate/circular placeholder.","triggerScenarios":"A resource bundle value contains a circular reference such as a=${b} and b=${a}, or the same placeholder is re-entered during nested resolution, when the bundle is first loaded and interpolated (e.g. on server or client startup reading err/resource messages).","commonSituations":"Editing seata .properties resource bundles and introducing a self-referencing or mutually-referencing placeholder chain; copy-paste mistakes like max.active=${max.active}.","solutions":["Search the loaded .properties bundle for the placeholder named in the message and break the cycle (make it reference a concrete value).","Check for self-reference like key=${key} and replace the right-hand side with a literal value.","Reload/restart the application after fixing the bundle so the cached parsed bundle is rebuilt."],"exampleFix":"# before\nmax.active=${max.active}\nthread.count=${thread.count}\nthread.count=${max.active}\n\n# after\nmax.active=50\nthread.count=50","handlingStrategy":"validation","validationCode":"// Before shipping bundles, detect circular placeholder references\nstatic void checkNoCircularPlaceholders(Map<String,String> props) {\n  for (String key : props.keySet()) {\n    Set<String> visited = new HashSet<>>();\n    String cur = key;\n    while (true) {\n      visited.add(cur);\n      Matcher m = Pattern.compile(\"\\\\$\\\\{([^}]+)\\\\}\").matcher(props.getOrDefault(cur, \"\"));\n      if (!m.find()) break;\n      String next = m.group(1);\n      if (!visited.add(next)) throw new IllegalStateException(\"Circular placeholder: \" + key);\n      cur = next;\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  ResourceBundleUtil.resolve(msg, params);\n} catch (SeataRuntimeException e) {\n  if (e.getCode() == ErrorCode.ERR_CONFIG && e.getMessage().contains(\"Duplicate placeholders\")) {\n    // log config file path + placeholder name; fail startup fast with a clear config error\n  }\n  throw e;\n}","preventionTips":["Lint resource bundles in CI for self- or mutually-referencing ${...} keys.","Never define key=${key} patterns, even as placeholders for future values.","Keep interpolation chains one or two levels deep."],"tags":["config","resource-bundle","placeholder","seata","startup"],"backgroundTag":null,"analyzedSha":"e01f97c6db397165050caa6764020410c2c8199a","analyzedAt":"2026-08-14T10:23:53.097Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}