apache/cassandra · error · ConfigurationException

Invalid annotations, you have more than one @Replaces…

Error message

Invalid annotations, you have more than one @Replaces annotation in Config class with same old name(<oldName>) defined.

What it means

Replacements.getNameReplacements() builds the map of deprecated->new config names from @Replaces annotations on Config fields. If two fields' @Replaces annotations declare the same oldName, the mapping would be ambiguous, so it throws ConfigurationException. This is a developer-facing error when authoring Config definitions, not an operator config problem.

Solutions

  1. Remove or change the duplicate @Replaces(oldName=...) so each old name maps to exactly one field
  2. Search Config.java for the offending old name to find the conflicting annotations
  3. If intentional, consolidate the migration logic into a single field's @Replaces

Example fix

// before
@Replaces("old_setting") public volatile int newSettingA;
@Replaces("old_setting") public volatile int newSettingB;
// after
@Replaces("old_setting") public volatile int newSettingA; // only one replaces each old name
Defensive patterns

Strategy: validation

Validate before calling

Set<String> seen = new HashSet<>();
for (Field f : Config.class.getDeclaredFields()) {
    Replaces r = f.getAnnotation(Replaces.class);
    if (r != null && !seen.add(r.oldName()))
        throw new IllegalStateException("Duplicate @Replaces oldName: " + r.oldName());
}

Try / catch

try { Map<String, Replacement> m = Replacements.getNameReplacements(...); }
catch (ConfigurationException e) { throw new IllegalStateException("Fix duplicate @Replaces annotations: " + e.getMessage()); }

Prevention

When it happens

Trigger: Adding a field to Config with @Replaces(oldName = "x") when another field already replaces oldName "x".

Common situations: Merging upstream patches that both introduce a replacement for the same deprecated setting; copy-pasting a @Replaces annotation without changing oldName; refactoring a renamed setting twice.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/c1a316e15b937adc. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/config/Replacements.java:52

    {
    }

    /**
     * @param klass to get replacements for
     * @return map of old names and replacements needed.
     */
    public static Map<Class<? extends Object>, Map<String, Replacement>> getNameReplacements(Class<? extends Object> klass)
    {
        List<Replacement> replacements = getReplacementsRecursive(klass);
        Map<Class<?>, Map<String, Replacement>> objectOldNames = new HashMap<>();
        for (Replacement r : replacements)
        {
            Map<String, Replacement> oldNames = objectOldNames.computeIfAbsent(r.parent, ignore -> new HashMap<>());
            if (!oldNames.containsKey(r.oldName))
                oldNames.put(r.oldName, r);
            else
            {
                throw new ConfigurationException("Invalid annotations, you have more than one @Replaces annotation in " +
                                                 "Config class with same old name(" + r.oldName + ") defined.");
            }
        }
        return objectOldNames;
    }

    /**
     * @param klass to get replacements for
     * @return map of old names and replacements needed.
     */
    private static List<Replacement> getReplacementsRecursive(Class<?> klass)
    {
        Set<Class<?>> seen = new HashSet<>(); // to make sure not to process the same type twice
        List<Replacement> accum = new ArrayList<>();
        getReplacementsRecursive(seen, accum, klass);
        return accum.isEmpty() ? Collections.emptyList() : accum;
    }

View on GitHub (pinned to 88fd0f6a0e)