apache/skywalking · error · IllegalArgumentException

Layer name must match [A-Z][A-Z0-9_]*: {}

Error message

Layer name must match [A-Z][A-Z0-9_]*: {}

What it means

IllegalArgumentException thrown by Layer.register when the layer name is null or does not match the pattern [A-Z][A-Z0-9_]* — it must start with an uppercase letter and contain only uppercase letters, digits, and underscores. This normalizes layer naming (it is persisted and surfaced in APIs/UI) and rejects camelCase, lowercase, hyphens, spaces, or leading digits.

Source

Thrown at oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java:356

     * loaders parsing inline {@code layerDefinitions:} blocks). Idempotent on identical
     * re-registration so the same extension loaded by multiple paths is harmless.
     *
     * @param name    upper-snake-case identifier; must match {@code [A-Z][A-Z0-9_]*}
     * @param value   ordinal unique across all layers (see class javadoc for the ordinal
     *                conventions and the {@code >= 1000} recommendation for extensions)
     * @param isNormal whether services in this layer are agent-installed (true) or conjectured (false)
     * @return the registered layer
     * @throws IllegalStateException     if the registry is sealed, or on a name/ordinal conflict
     * @throws IllegalArgumentException  if name shape is invalid
     */
    public static synchronized Layer register(final String name, final int value, final boolean isNormal) {
        if (SEALED) {
            throw new IllegalStateException(
                "Layer registry is sealed; cannot register " + name + "=" + value
                    + ". External layers must register before CoreModule.notifyAfterCompleted().");
        }
        if (name == null || !NAME_PATTERN.matcher(name).matches()) {
            throw new IllegalArgumentException(
                "Layer name must match [A-Z][A-Z0-9_]*: " + name);
        }
        final Layer existingByName = BY_NAME.get(name);
        if (existingByName != null) {
            if (existingByName.value == value && existingByName.isNormal == isNormal) {
                return existingByName;
            }
            throw new IllegalStateException(
                "Layer name conflict: " + name + " already registered as ordinal=" + existingByName.value
                    + ", normal=" + existingByName.isNormal
                    + "; refused re-registration as ordinal=" + value + ", normal=" + isNormal);
        }
        final Layer existingByValue = BY_VALUE.get(value);
        if (existingByValue != null) {
            throw new IllegalStateException(
                "Layer ordinal conflict at " + value
                    + ": existing=" + existingByValue.name + ", new=" + name);
        }

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Normalize the name to upper-snake-case before registering: uppercase, replace non-alphanumerics with '_', ensure it starts with A-Z
  2. Validate names at config-load time and fail with a clear plugin-level message rather than deep in Layer.register
  3. If the name comes from external input, reject/sanitize it explicitly (e.g. name.toUpperCase(Locale.ROOT).replaceAll("[^A-Z0-9]+","_")) and ensure first char is a letter

Example fix

// before
Layer.register("my-layer", 1200, false); // hyphen -> IllegalArgumentException

// after
Layer.register("MY_LAYER", 1200, false);
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern LAYER_NAME = Pattern.compile("[A-Z][A-Z0-9_]*");
String safe = raw == null ? null : raw.toUpperCase(Locale.ROOT).replaceAll("[^A-Z0-9]+", "_");
if (safe == null || !LAYER_NAME.matcher(safe).matches() || Character.isDigit(safe.charAt(0))) {
    throw new IllegalArgumentException("Layer name must match [A-Z][A-Z0-9_]*: " + raw);
}

Type guard

boolean isValidLayerName(String name) {
    return name != null && name.matches("[A-Z][A-Z0-9_]*");
}

Prevention

When it happens

Trigger: Calling Layer.register with names like 'myLayer', 'my-layer', 'My_Layer', '9LAYER', 'MY LAYER', or null. Common when a plugin derives the name from configuration or a protocol string without normalizing to upper-snake-case.

Common situations: Custom extension plugins taking layer names from user config or telemetry labels verbatim; porting older code that used arbitrary strings before the pattern was enforced; typos in static registrations.

Related errors


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