apache/beam · error · java.lang.RuntimeException

Builder method has to be explicitly allowed

Error message

Builder method  has to be explicitly allowed

What it means

The builder method matched by its @MultiLanguageBuilderMethod name, but that method is not on the expansion service's allowlist, so the service refuses to invoke it. The allowlist (isAllowedBuilderMethod) is a security control preventing arbitrary code execution through the expansion service.

Solutions

  1. Add the builder method name to the expansion service's allowed-builder-methods allowlist configuration and restart the service.
  2. If you control the transform, use the standard annotated-constructor/builder registration expected by the service so it is allowlisted.
  3. Check the allowlist configuration file/format (allowed class/method list) for typos in the method name.

Example fix

// before: expansion service started without allowlist entry
java -jar expansion.jar
// after
java -jar expansion.jar --allowedBuilderMethods=com.example.MyTransform:withCount
Defensive patterns

Strategy: validation

Validate before calling

ExpansionServiceConfig cfg = service.getConfig();
if (!cfg.getAllowedBuilderMethods(transformClass).contains(annotatedMethodName)) {
  throw new IllegalStateException("Builder method must be allowlisted: " + annotatedMethodName);
}

Try / catch

try {
  return getTransform(payload);
} catch (RuntimeException e) {
  if (e.getMessage() != null && e.getMessage().contains("has to be explicitly allowed")) {
    throw new SecurityException("Builder method not allowlisted on expansion service: " + payload.getName(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Requesting expansion of a transform whose payload names a @MultiLanguageBuilderMethod-annotated method that was not explicitly added to the configured allowlist of the expansion service.

Common situations: Using XLang (e.g. Python/Go calling a Java transform) without registering the builder method in the expansion service's allowed builder methods configuration; adding new builder methods to a transform but forgetting to update the allowlist.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/8c38d08c6cf20d1b. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/expansion-service/src/main/java/org/apache/beam/sdk/expansion/service/JavaClassLookupTransformProvider.java:182

                + " with parameter schema "
                + builderMethod.getSchema(),
            e);
      }
    }

    return transform;
  }

  private boolean isBuilderMethodForName(
      Method method, String nameFromPayload, AllowedClass allowListClass) {
    // Lookup based on method annotations
    for (Annotation annotation : method.getAnnotations()) {
      if (annotation instanceof MultiLanguageBuilderMethod) {
        if (nameFromPayload.equals(((MultiLanguageBuilderMethod) annotation).name())) {
          if (allowListClass.isAllowedBuilderMethod(nameFromPayload)) {
            return true;
          } else {
            throw new RuntimeException(
                "Builder method " + nameFromPayload + " has to be explicitly allowed");
          }
        }
      }
    }

    // Lookup based on the method name.
    boolean match = method.getName().equals(nameFromPayload);
    String consideredMethodName = method.getName();

    // We provide a simplification for common Java builder pattern naming convention where builder
    // methods start with "with". In this case, for a builder method name in the form "withXyz",
    // users may just use "xyz". If additional updates to the method name are needed the transform
    // has to be updated by adding annotations.
    if (!match && consideredMethodName.length() > 4 && consideredMethodName.startsWith("with")) {
      consideredMethodName =
          consideredMethodName.substring(4, 5).toLowerCase() + consideredMethodName.substring(5);
      match = consideredMethodName.equals(nameFromPayload);

View on GitHub (pinned to 12126d8942)