apache/skywalking · critical · IllegalStateException

Duplicate RuleEngine registration for catalog '{catalog}': {

Error message

Duplicate RuleEngine registration for catalog '{catalog}': {priorClassName} vs {engineClassName}

What it means

RuleEngineRegistry.register throws IllegalStateException at module start when two distinct RuleEngine implementations both claim the same catalog via supportedCatalogs(). The registry deliberately fails fast: silently dropping one engine would make catalog behavior depend on registration order, so duplicate catalog ownership is treated as a configuration error worth aborting module start. The message names the prior engine class and the new one for diagnosis.

Source

Thrown at oap-server/server-admin/runtime-rule/src/main/java/org/apache/skywalking/oap/server/receiver/runtimerule/engine/RuleEngineRegistry.java:48

 * to the right engine in O(1).
 *
 * <p>Adding a new DSL is one line in {@link
 * org.apache.skywalking.oap.server.receiver.runtimerule.module.RuntimeRuleModuleProvider}:
 * register the engine instance with this registry. No scheduler edit required.
 */
public final class RuleEngineRegistry {
    private final Map<String, RuleEngine<?>> byCatalog = new HashMap<>();

    /**
     * Register {@code engine} for every catalog it claims. Throws {@link IllegalStateException}
     * on duplicate catalog: two engines competing for the same catalog is a configuration error
     * worth failing module start over rather than silently dropping one.
     */
    public void register(final RuleEngine<?> engine) {
        for (final String catalog : engine.supportedCatalogs()) {
            final RuleEngine<?> prior = byCatalog.put(catalog, engine);
            if (prior != null && prior != engine) {
                throw new IllegalStateException(
                    "Duplicate RuleEngine registration for catalog '" + catalog
                        + "': " + prior.getClass().getName() + " vs " + engine.getClass().getName());
            }
        }
    }

    /**
     * @return the engine registered for {@code catalog}, or {@code null} if none. The scheduler
     *     treats {@code null} as a hard error (catalog should never be loaded if no engine claims
     *     it — the static rule registry filters by supported catalog at boot).
     */
    public RuleEngine<?> forCatalog(final String catalog) {
        return byCatalog.get(catalog);
    }

    /** All distinct engines, for module-start logging and lifecycle wiring. */
    public Collection<RuleEngine<?>> engines() {
        return byCatalog.values();

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Make catalogs disjoint: edit one engine's supportedCatalogs() to claim a catalog only it owns, or disable/remove the redundant engine module
  2. Check for duplicate engine classes on the classpath (mvn dependency:tree / jar overlap) and exclude the stale artifact
  3. If you intended to replace the default engine, exclude it from the module/provider list rather than registering both
  4. Restart the OAP after fixing — this throws during start and the module will not come up half-initialized

Example fix

// before
public Set<String> supportedCatalogs() { return Set.of("lal", "lal-v2"); } // 'lal' clashes with built-in
// after
public Set<String> supportedCatalogs() { return Set.of("lal-v2"); }
Defensive patterns

Strategy: validation

Validate before calling

final Set<String> claimed = new HashSet<>();
for (final RuleEngine<?> e : engines) {
    for (final String c : e.supportedCatalogs())
        if (!claimed.add(c)) throw new ConfigException("duplicate catalog " + c);
}

Try / catch

catch (IllegalStateException e) when 'Duplicate RuleEngine registration': abort startup — this is a packaging/config defect; dedupe the classpath or narrow supportedCatalogs() before restart.

Prevention

When it happens

Trigger: Boot-time SPI/module wiring where two RuleEngine beans overlap in supportedCatalogs() — e.g. a custom engine added alongside the built-in LalRuleEngine/MalRuleEngine both declaring 'lal', or two versions of an engine class on the classpath (shaded duplicate dependency).

Common situations: Custom OAP build adding a bespoke engine for an existing catalog without excluding the default one; Dependency conflict bundling two copies of the runtime-rule engine module with different class names; Copy-pasting an engine class and forgetting to change its catalog claims

Related errors


AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14). Data as JSON: /api/errors/00e5c7ba9ce6089a. Report an issue: GitHub.