{"record":{"id":"482e188dab8ef502","repo":"apache/skywalking","slug":"layer-registry-is-sealed-cannot-register-e","errorCode":null,"errorMessage":"Layer registry is sealed; cannot register {}={}. External layers must register before CoreModule.notifyAfterCompleted().","messagePattern":"Layer registry is sealed; cannot register (.+?)=(.+?)\\. External layers must register before CoreModule\\.notifyAfterCompleted\\(\\)\\.","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":351,"sourceCode":"    }\n\n    /**\n     * Single registration path used by both the built-in static initializer above and by every\n     * external source ({@code LayerExtensionLoader} for operator yaml + SPI, and the MAL/LAL DSL\n     * loaders parsing inline {@code layerDefinitions:} blocks). Idempotent on identical\n     * re-registration so the same extension loaded by multiple paths is harmless.\n     *\n     * @param name    upper-snake-case identifier; must match {@code [A-Z][A-Z0-9_]*}\n     * @param value   ordinal unique across all layers (see class javadoc for the ordinal\n     *                conventions and the {@code >= 1000} recommendation for extensions)\n     * @param isNormal whether services in this layer are agent-installed (true) or conjectured (false)\n     * @return the registered layer\n     * @throws IllegalStateException     if the registry is sealed, or on a name/ordinal conflict\n     * @throws IllegalArgumentException  if name shape is invalid\n     */\n    public static synchronized Layer register(final String name, final int value, final boolean isNormal) {\n        if (SEALED) {\n            throw new IllegalStateException(\n                \"Layer registry is sealed; cannot register \" + name + \"=\" + value\n                    + \". 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);","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java#L333-L369","documentation":"IllegalStateException thrown by Layer.register(name, value, isNormal) when the static layer registry has already been sealed (CoreModule.notifyAfterCompleted() sets SEALED). The registry seals after OAP boot so layer ordinals stay stable for stored data; any extension trying to register a new layer afterwards is rejected with instructions in the message itself.","triggerScenarios":"Calling Layer.register after the OAP core module finished booting — e.g. a receiver/analyzer plugin registering its custom layer from start() code that runs after CoreModule.notifyAfterCompleted(), a dynamically loaded extension, or application code registering lazily on first data.","commonSituations":"Writing a custom receiver plugin that adds a layer (e.g. MESH or a proprietary tier) but performs registration late in the module lifecycle; OAL/observer code that defers registration until first telemetry; unit tests that boot the full module manager before registering test layers.","solutions":["Move Layer.register into the provider's prepare()/start() phase so it executes before CoreModule.notifyAfterCompleted() seals the registry","For post-boot needs, use registerDynamic/unregisterDynamic which are explicitly allowed after sealing (per the javadoc on the seal method)","In tests, register the layer before moduleManager.init()/notifyAfterCompleted(), or reset/avoid sealing in the test harness","Check the exception message: it names the layer and ordinal you tried, confirming which registration raced the seal"],"exampleFix":"// before (registered too late — first request)\npublic class MyListener implements SpanListener {\n  private static final Layer MY_LAYER = Layer.register(\"MY_LAYER\", 1200, false); // after seal -> throws\n}\n\n// after (registered at provider start, before core seals)\npublic class MyModuleProvider extends ModuleProvider {\n  public void prepare() {\n    Layer.register(\"MY_LAYER\", 1200, false); // boot-time, registry open\n  }\n}","handlingStrategy":"validation","validationCode":"static final Layer MY_LAYER = Layer.register(\"MY_LAYER\", 1200, false); // class-load / prepare()-time\n// guard any late path:\nif (!Layer.listAllLayers().stream().anyMatch(l -> l.name().equals(\"MY_LAYER\"))) {\n    Layer.registerDynamic(\"MY_LAYER\", 1200, false); // post-seal API\n}","typeGuard":null,"tryCatchPattern":"try {\n    Layer.register(name, value, isNormal);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"sealed\")) {\n        Layer.registerDynamic(name, value, isNormal); // allowed after seal\n    } else {\n        throw e;\n    }\n}","preventionTips":["Register extension layers in ModuleProvider.prepare()/start(), never lazily on first data or in listeners","Reserve static final Layer constants initialized at plugin class-load time","In tests, register layers before moduleManager.notifyAfterCompleted() or design the test harness to skip sealing","Post-boot registration must use registerDynamic/unregisterDynamic"],"tags":["layer-registry","plugin-lifecycle","startup","oap-server"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}