apache/skywalking · error · ApplyException

LAL register failed for rule '{ruleName}' in {sourceName}

Error message

LAL register failed for rule '{ruleName}' in {sourceName}

What it means

ApplyException thrown when the per-rule registration loop (factory.addOrReplace) fails for an LAL rule during phase 2 of LalFileApplier.apply. Distinct from the compile error: compilation succeeded, but swapping the compiled rule into the live listener Factory threw — commonly the cross-file collision guard (another LAL file already owns (layer, ruleName)) or an internal registry failure. Unlike the compile case, rules registered before the failure have landed; they are returned in the exception's partial list so the caller can unwind them.

Source

Thrown at oap-server/server-admin/runtime-rule/src/main/java/org/apache/skywalking/oap/server/receiver/runtimerule/apply/LalFileApplier.java:209

                    t, Collections.emptyList());
            }
        }

        final List<RegisteredRule> registered = new ArrayList<>();
        for (final LogFilterListener.Factory.CompiledLAL x : compiled) {
            try {
                // Cross-file collision guard: if another LAL file already owns (layer,
                // ruleName), and we're not the prior holder (which would be a self-replace),
                // reject — the registry's uniqueness invariant is per-layer within the
                // cluster. Self-replace is safe because Phase 1 already succeeded and
                // addOrReplace is the intended atomic takeover.
                factory.addOrReplace(x);
                registered.add(new RegisteredRule(x.layer, x.ruleName));
            } catch (final Throwable t) {
                // Roll back registrations made so far AND the layer claims. Rule-registration
                // partial state survives in the caller's `partial` list for unwinding.
                layerRegistry.rollback(appliedClaims);
                throw new ApplyException(
                    "LAL register failed for rule '" + x.ruleName + "' in " + sourceName,
                    t, Collections.unmodifiableList(new ArrayList<>(registered)));
            }
        }
        return new Applied(sourceName, Collections.unmodifiableList(registered), ruleLoader,
                           appliedClaims);
    }

    /** Split {@code "catalog/name"} → catalog half. Falls back to {@code lal} for bare
     *  source names (legacy / test callers). */
    private static String deriveCatalog(final String sourceName) {
        final int idx = sourceName.indexOf('/');
        return idx > 0 ? sourceName.substring(0, idx) : "lal";
    }

    private static String deriveRuleName(final String sourceName) {
        final int idx = sourceName.indexOf('/');
        return idx > 0 ? sourceName.substring(idx + 1) : sourceName;

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Rename the colliding rule or move it to a different layer so (layer, ruleName) is unique cluster-wide
  2. If the intent was replacement, apply the update through the SAME source file/key that owns the rule (sourceName keys the registry) rather than a second file
  3. Delete or update the prior-owning file first, then apply the new one
  4. If the failure was transient (concurrent peer apply), retry after the cluster settles — the applier already rolled back layer claims and returned the partial set for cleanup
Defensive patterns

Strategy: try-catch

Validate before calling

// Before apply, ensure (layer, ruleName) uniqueness across all installed files
Set<String> owned = enumerateAllInstalledLalKeys(); // layer:name pairs from every file
for (final String k : newFileKeys) if (owned.contains(k) && !sameSourceFile) throw new ClientSideValidationException("collision: " + k);

Try / catch

catch (ApplyException e) with 'LAL register failed': unwind the partial registrations named in e.getPartiallyRegistered() via the admin uninstall, then fix the (layer,name) collision and re-apply.

Prevention

When it happens

Trigger: Hot-updating an LAL file where rule 'X' in layer 'general' is also declared in a DIFFERENT runtime file or a disk-loaded file on a peer — addOrReplace rejects non-self takeover. Also possible when the LogAnalyzer Factory's internal addOrReplace throws for resource reasons (e.g. classloader retire races).

Common situations: Two runtime LAL files declare the same rule name in the same layer (copy-paste duplication); Re-applying a file while a peer OAP node concurrently applied a colliding file; A disk-loaded lal/*.yaml and a runtime rule collide on (layer, name) because the runtime sourceName derived a key equal to the disk twin's

Related errors


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