{"record":{"id":"c4ac36a8a368f747","repo":"apache/skywalking","slug":"layer-ordinal-conflict-at-existing-new","errorCode":null,"errorMessage":"Layer ordinal conflict at {}: existing={}, new={}","messagePattern":"Layer ordinal conflict at (.+?): existing=(.+?), new=(.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java","lineNumber":371,"sourceCode":"                    + \". External layers must register before CoreModule.notifyAfterCompleted().\");\n        }\n        if (name == null || !NAME_PATTERN.matcher(name).matches()) {\n            throw new IllegalArgumentException(\n                \"Layer name must match [A-Z][A-Z0-9_]*: \" + name);\n        }\n        final Layer existingByName = BY_NAME.get(name);\n        if (existingByName != null) {\n            if (existingByName.value == value && existingByName.isNormal == isNormal) {\n                return existingByName;\n            }\n            throw new IllegalStateException(\n                \"Layer name conflict: \" + name + \" already registered as ordinal=\" + existingByName.value\n                    + \", normal=\" + existingByName.isNormal\n                    + \"; refused re-registration as ordinal=\" + value + \", normal=\" + isNormal);\n        }\n        final Layer existingByValue = BY_VALUE.get(value);\n        if (existingByValue != null) {\n            throw new IllegalStateException(\n                \"Layer ordinal conflict at \" + value\n                    + \": existing=\" + existingByValue.name + \", new=\" + name);\n        }\n        final Layer layer = new Layer(name, value, isNormal);\n        BY_VALUE.put(value, layer);\n        BY_NAME.put(name, layer);\n        return layer;\n    }\n\n    /**\n     * Closes the registry. Subsequent {@link #register} calls throw; only\n     * {@link #registerDynamic} / {@link #unregisterDynamic} can mutate the registry after\n     * seal. Called by {@code CoreModuleProvider.notifyAfterCompleted()} after every\n     * module's prepare/start has run, so MAL/LAL/SPI/yaml all had their full window.\n     * Idempotent.\n     */\n    public static synchronized void seal() {\n        rebuildCachedValues();","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java#L353-L389","documentation":"IllegalStateException thrown by Layer.register when the requested ordinal (value) is already taken by a different layer name. Ordinals are persisted with telemetry data, so they must be globally unique; the message names both the existing layer at that ordinal and the new name that attempted to claim it. The class javadoc recommends extensions use ordinals >= 1000 to avoid colliding with built-in layers.","triggerScenarios":"Registering Layer.register(\"NEW_LAYER\", 2, true) when ordinal 2 belongs to a built-in layer (e.g. MESH/database layers occupy low numbers); or two extensions both picking the same arbitrary value like 1000. BY_VALUE lookup detects the clash.","commonSituations":"Extension authors choosing small ordinals that collide with core layers; multiple third-party plugins in one OAP both hardcoding 1000; upgrading an extension whose ordinal now overlaps a newly added built-in layer.","solutions":["Choose an extension ordinal >= 1000 and coordinate between installed extensions (e.g. allocate a distinctive range like 1200, 1250, ...) to avoid mutual collisions","If the ordinal is meant to identify an existing layer, register the existing NAME with that exact value instead of a new name (identical name+value+isNormal is idempotent), or just reference the existing Layer constant","Read the message to see which layer owns the ordinal, then pick a free one","For shipped plugins, treat ordinal changes as breaking: keep the value stable across releases"],"exampleFix":"// before\nLayer.register(\"MY_LAYER\", 2, false); // 2 already used by a built-in layer -> throws\n\n// after\nLayer.register(\"MY_LAYER\", 1200, false); // extension range >= 1000, unique","handlingStrategy":"validation","validationCode":"// coordinate ordinals: extensions should claim unique values >= 1000\nprivate static final int MY_LAYER_ORDINAL = 1237;\nif (Layer.valueOf(MY_LAYER_ORDINAL) != null) { // occupied\n    throw new IllegalStateException(\"Ordinal \" + MY_LAYER_ORDINAL + \" already claimed; allocate another >= 1000\");\n}\nLayer.register(\"MY_LAYER\", MY_LAYER_ORDINAL, false);","typeGuard":null,"tryCatchPattern":"try {\n    Layer.register(name, value, isNormal);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"ordinal conflict\")) {\n        throw new IllegalStateException(\"Layer ordinal \" + value + \" taken by \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Use ordinals >= 1000 for extensions as the class javadoc recommends","Document an ordinal allocation table for all third-party extensions deployed in the same OAP","Treat an extension's ordinal as frozen ABI once shipped — changing it breaks stored telemetry","Prefer referencing an existing Layer constant when you meant a built-in layer instead of re-registering a new name at its value"],"tags":["layer-registry","conflict","ordinal","plugin-development","oap-server"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}