apache/beam · error · RuntimeException
Unable to construct @OnTimerFamily invoker for ${className}
Error message
Unable to construct @OnTimerFamily invoker for ${className} What it means
ByteBuddyOnTimerInvokerFactory.forTimerFamily does for @OnTimerFamily methods what forTimer does for @OnTimer: it generates an invoker class with ByteBuddy and instantiates it reflectively. Failures in generation, classloading, constructor access, or invocation (including the same six checked/reflective exception types) are wrapped in this RuntimeException naming the DoFn class. It indicates the timer-family dispatch machinery could not be built for that DoFn.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/ByteBuddyOnTimerInvokerFactory.java:104
public <InputT, OutputT> OnTimerInvoker<InputT, OutputT> forTimerFamily(
DoFn<InputT, OutputT> fn, String timerId) {
@SuppressWarnings("unchecked")
Class<? extends DoFn<?, ?>> fnClass = (Class<? extends DoFn<?, ?>>) fn.getClass();
try {
OnTimerMethodSpecifier onTimerMethodSpecifier =
OnTimerMethodSpecifier.forClassAndTimerId(fnClass, timerId);
Constructor<?> constructor = constructorTimerFamilyCache.get(onTimerMethodSpecifier);
return (OnTimerInvoker<InputT, OutputT>) constructor.newInstance(fn);
} catch (InstantiationException
| IllegalAccessException
| IllegalArgumentException
| InvocationTargetException
| SecurityException
| ExecutionException e) {
throw new RuntimeException(
String.format(
"Unable to construct @%s invoker for %s",
DoFn.OnTimerFamily.class.getSimpleName(), fn.getClass().getName()),
e);
}
}
public static ByteBuddyOnTimerInvokerFactory only() {
return INSTANCE;
}
private static final ByteBuddyOnTimerInvokerFactory INSTANCE =
new ByteBuddyOnTimerInvokerFactory();
private ByteBuddyOnTimerInvokerFactory() {}
/**
* The field name for the delegate of {@link DoFn} subclass that a bytebuddy invoker will call.View on GitHub (pinned to 12126d8942)
Solutions
- Declare the DoFn as a public top-level (or public static nested) class with a visible constructor.
- Inspect getCause() of the thrown RuntimeException to pinpoint generation vs instantiation failure.
- Upgrade Beam (and its bundled ByteBuddy) or set -Dnet.bytebuddy.experimental=true for new JDK compatibility.
- Verify the @OnTimerFamily method's timer family ID matches a @TimerFamily field declaration in the DoFn.
Example fix
// before: private nested class
private class FamilyFn extends DoFn<KV<String,Integer>, String> {
@TimerFamily("tf") private final TimerSpec spec = TimerSpecs.timerMap(...);
@OnTimerFamily("tf") public void on(Map<String,Timer> ts) {...}
}
// after: public static class with public members
public static class FamilyFn extends DoFn<KV<String,Integer>, String> {
@TimerFamily("tf") public final TimerSpec spec = TimerSpecs.timerMap(...);
@OnTimerFamily("tf") public void on(Map<String,Timer> ts) {...}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!Modifier.isPublic(fn.getClass().getModifiers()) || fn.getClass().isAnonymousClass()) {
throw new IllegalStateException("@OnTimerFamily DoFn must be public and instantiable");
}
boolean hasFamilyMethod = java.util.Arrays.stream(fn.getClass().getDeclaredMethods())
.anyMatch(m -> m.isAnnotationPresent(DoFn.OnTimerFamily.class));
if (!hasFamilyMethod) throw new IllegalStateException("No @OnTimerFamily method on " + fn.getClass()); Try / catch
try {
invoker = ByteBuddyOnTimerInvokerFactory.forTimerFamily(fn, timerFamilyId);
} catch (RuntimeException e) {
log.error("@OnTimerFamily invoker construction failed for " + fn.getClass().getName()
+ "; cause=" + e.getCause(), e);
throw e;
} Prevention
- Use public top-level/static DoFn classes with accessible constructors
- Ensure @OnTimerFamily IDs match declared @TimerFamily specs
- Validate timer-family pipelines under the production runner's classloader settings in CI
- Align Beam/ByteBuddy versions with the JDK used at runtime
When it happens
Trigger: Calling forTimerFamily (or triggering a timer family) for a DoFn whose @OnTimerFamily invoker cannot be generated or constructed: inaccessible/non-static DoFn class, ByteBuddy generation failure on the JVM, or reflective instantiation throwing any of the wrapped exception types.
Common situations: DoFn with @OnTimerFamily defined as a private or inner class; runners with restrictive classloading (Flink user-code classloader) that cannot see generated classes; newer JDKs unsupported by the bundled ByteBuddy version; missing TimerFamily spec registration in the DoFn signature.
Related errors
- Unable to construct @OnTimer invoker for ${className}
- Failed to locate DefaultGetSize.validateSize()
- Unhandled type as method argument: ${type}
- error when invoking Coder factory method
- cannot register Coder : does not have an accessible method n
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/f2a530ceadd2661b.
Report an issue: GitHub.