apache/beam · error · RuntimeException

Unable to bind invoker for ${fnClass}

Error message

Unable to bind invoker for ${fnClass}

What it means

When creating a DoFn invoker, ByteBuddy generates an invoker class and instantiates it reflectively with the DoFn instance. If that instantiation throws (bad access, wrong arguments, security manager, reflective target failure), it is wrapped in this RuntimeException naming the DoFn class.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyDoFnInvokerFactory.java:381

              onTimerMethod.id(), OnTimerInvokers.forTimer(fn, onTimerMethod.id()));
        }
      }

      if (signature.onTimerFamilyMethods() != null) {
        for (DoFnSignature.OnTimerFamilyMethod onTimerFamilyMethod :
            signature.onTimerFamilyMethods().values()) {
          invoker.addOnTimerFamilyInvoker(
              onTimerFamilyMethod.id(),
              OnTimerInvokers.forTimerFamily(fn, onTimerFamilyMethod.id()));
        }
      }
      return invoker;
    } catch (InstantiationException
        | IllegalAccessException
        | IllegalArgumentException
        | InvocationTargetException
        | SecurityException e) {
      throw new RuntimeException("Unable to bind invoker for " + fn.getClass(), e);
    }
  }

  private <InputT, OutputT> Constructor<?> getByteBuddyInvokerConstructor(
      DoFnSignature signature, DoFn<InputT, OutputT> fn) {
    // Extract input and output type descriptors to distinguish generic instantiations.
    // Fall back to Object.class if unavailable. When type info is lost, different generic
    // instantiations share an invoker, which is acceptable since the DoFn class in the cache
    // key prevents collisions between different DoFn classes.
    TypeDescriptor<InputT> inputType;
    try {
      inputType = fn.getInputTypeDescriptor();
    } catch (Exception e) {
      // Some DoFns (like MapElements) throw IllegalStateException if queried after
      // serialization.
      // In this case, we fall back to the raw class behavior (Object).
      inputType = null;
    }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Add JVM flags --add-opens java.base/java.lang=ALL-UNNAMED (and related packages) or use a JDK supported by your Beam/ByteBuddy versions.
  2. Inspect the chained cause for the real error (access vs security vs instantiation) and fix that specifically.
  3. Avoid custom classloaders that cannot see the generated invoker classes; run on the same classloader that built the pipeline.
  4. Upgrade beam-sdks-java-core / byte-buddy to versions compatible with your platform.

Example fix

// before
java -jar app.jar // RuntimeException wrapping InstantiationException on Java 17
// after
java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED -jar app.jar
Defensive patterns

Strategy: try-catch

Try / catch

try {
  DoFnInvoker invoker = ByteBuddyDoFnInvokerFactory.newByteBuddyInvoker(fn);
} catch (RuntimeException e) {
  LOG.error("Failed to bind invoker for " + fn.getClass(), e.getCause());
  throw e;
}

Prevention

When it happens

Trigger: newByteBuddyInvoker fails while invoking the generated invoker's constructor — generated class inaccessible to the classloader, reflection blocked, or the constructor rejected the argument.

Common situations: Java 9+ JPMS strong encapsulation without --add-opens; SecurityManager or container classloader isolation (OSGi, app servers); outdated ByteBuddy on a newer JDK.

Related errors


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