{"record":{"id":"00e5c7ba9ce6089a","repo":"apache/skywalking","slug":"duplicate-ruleengine-registration-for-catalog-ca","errorCode":null,"errorMessage":"Duplicate RuleEngine registration for catalog '{catalog}': {priorClassName} vs {engineClassName}","messagePattern":"Duplicate RuleEngine registration for catalog '(.+?)': (.+?) vs (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"oap-server/server-admin/runtime-rule/src/main/java/org/apache/skywalking/oap/server/receiver/runtimerule/engine/RuleEngineRegistry.java","lineNumber":48,"sourceCode":" * to the right engine in O(1).\n *\n * <p>Adding a new DSL is one line in {@link\n * org.apache.skywalking.oap.server.receiver.runtimerule.module.RuntimeRuleModuleProvider}:\n * register the engine instance with this registry. No scheduler edit required.\n */\npublic final class RuleEngineRegistry {\n    private final Map<String, RuleEngine<?>> byCatalog = new HashMap<>();\n\n    /**\n     * Register {@code engine} for every catalog it claims. Throws {@link IllegalStateException}\n     * on duplicate catalog: two engines competing for the same catalog is a configuration error\n     * worth failing module start over rather than silently dropping one.\n     */\n    public void register(final RuleEngine<?> engine) {\n        for (final String catalog : engine.supportedCatalogs()) {\n            final RuleEngine<?> prior = byCatalog.put(catalog, engine);\n            if (prior != null && prior != engine) {\n                throw new IllegalStateException(\n                    \"Duplicate RuleEngine registration for catalog '\" + catalog\n                        + \"': \" + prior.getClass().getName() + \" vs \" + engine.getClass().getName());\n            }\n        }\n    }\n\n    /**\n     * @return the engine registered for {@code catalog}, or {@code null} if none. The scheduler\n     *     treats {@code null} as a hard error (catalog should never be loaded if no engine claims\n     *     it — the static rule registry filters by supported catalog at boot).\n     */\n    public RuleEngine<?> forCatalog(final String catalog) {\n        return byCatalog.get(catalog);\n    }\n\n    /** All distinct engines, for module-start logging and lifecycle wiring. */\n    public Collection<RuleEngine<?>> engines() {\n        return byCatalog.values();","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-admin/runtime-rule/src/main/java/org/apache/skywalking/oap/server/receiver/runtimerule/engine/RuleEngineRegistry.java#L30-L66","documentation":"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.","triggerScenarios":"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).","commonSituations":"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","solutions":["Make catalogs disjoint: edit one engine's supportedCatalogs() to claim a catalog only it owns, or disable/remove the redundant engine module","Check for duplicate engine classes on the classpath (mvn dependency:tree / jar overlap) and exclude the stale artifact","If you intended to replace the default engine, exclude it from the module/provider list rather than registering both","Restart the OAP after fixing — this throws during start and the module will not come up half-initialized"],"exampleFix":"// before\npublic Set<String> supportedCatalogs() { return Set.of(\"lal\", \"lal-v2\"); } // 'lal' clashes with built-in\n// after\npublic Set<String> supportedCatalogs() { return Set.of(\"lal-v2\"); }","handlingStrategy":"validation","validationCode":"final Set<String> claimed = new HashSet<>();\nfor (final RuleEngine<?> e : engines) {\n    for (final String c : e.supportedCatalogs())\n        if (!claimed.add(c)) throw new ConfigException(\"duplicate catalog \" + c);\n}","typeGuard":null,"tryCatchPattern":"catch (IllegalStateException e) when 'Duplicate RuleEngine registration': abort startup — this is a packaging/config defect; dedupe the classpath or narrow supportedCatalogs() before restart.","preventionTips":["Assert catalog disjointness in an integration test over the full engine set","Exclude default engine modules when adding a custom engine for the same catalog","Check mvn dependency:tree for duplicate runtime-rule artifacts before shipping custom distributions"],"tags":["configuration","registry","engine","module-start"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}