apache/beam · error · java.lang.IllegalArgumentException
is not permitted for user timers
Error message
%s is not permitted for user timers
What it means
Beam's user-facing Timer API supports EVENT_TIME and PROCESSING_TIME domains only. timerFamilyTimeDomainToProto maps the Java TimeDomain enum to the proto enum, and SYNCHRONIZED_PROCESSING_TIME — an internal domain used by runners/test streams — is explicitly rejected for user timers with this IllegalArgumentException.
Solutions
- Change the timer to TimeDomain.EVENT_TIME or TimeDomain.PROCESSING_TIME
- Only use SYNCHRONIZED_PROCESSING_TIME in runner/test-stream internals, not user DoFn timers
- Review the @TimerFamily signature and the TimeDomain import used in the DoFn
Example fix
// before
@TimerFamily("alarm") Timer family; // declared with TimeDomain.SYNCHRONIZED_PROCESSING_TIME
// after
timer = dofnTimer.withTimeDomain(TimeDomain.EVENT_TIME); // or PROCESSING_TIME Defensive patterns
Strategy: validation
Validate before calling
if (timeDomain == TimeDomain.SYNCHRONIZED_PROCESSING_TIME) throw new IllegalArgumentException("SYNCHRONIZED_PROCESSING_TIME not allowed for user timers"); Type guard
boolean isUserTimerDomain(TimeDomain d) { return d == TimeDomain.EVENT_TIME || d == TimeDomain.PROCESSING_TIME; } Try / catch
try { String proto = ParDoTranslation.timerFamilyTimeDomainToProto(timeDomain); } catch (IllegalArgumentException e) { /* switch to EVENT_TIME */ } Prevention
- Only declare timers with EVENT_TIME or PROCESSING_TIME
- Never copy TimeDomain constants from runner/test-stream internals
- Document allowed domains on custom TimerSpec builders
When it happens
Trigger: Defining or translating a @Timer whose TimeDomain parameter is TimeDomain.SYNCHRONIZED_PROCESSING_TIME (e.g. via TimerSpec/@TimerFamily or a DoFn signature) so translateTimerFamilySpec hits the forbidden case.
Common situations: Copy-pasting timer signatures from runner internals/test-stream code; constructing a TimerSpec programmatically with the wrong TimeDomain constant; migrating code that used internal timer APIs.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Cannot access fire timestamp outside of @OnTimer method.
- Cannot access OnTimerContext outside of @OnTimer methods.
- Cannot access time domain outside of @ProcessTimer method.
- Cannot access timer in non-window observing context.
- Cannot access timerFamily in non-window observing context.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/962920a54980beeb.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/util/construction/ParDoTranslation.java:746
TimerSpec timer,
SdkComponents components,
Coder<?> keyCoder,
Coder<BoundedWindow> windowCoder) {
return RunnerApi.TimerFamilySpec.newBuilder()
.setTimeDomain(translateTimeDomain(timer.getTimeDomain()))
.setTimerFamilyCoderId(
registerCoderOrThrow(components, Timer.Coder.of(keyCoder, windowCoder)))
.build();
}
public static RunnerApi.TimeDomain.Enum translateTimeDomain(TimeDomain timeDomain) {
switch (timeDomain) {
case EVENT_TIME:
return RunnerApi.TimeDomain.Enum.EVENT_TIME;
case PROCESSING_TIME:
return RunnerApi.TimeDomain.Enum.PROCESSING_TIME;
case SYNCHRONIZED_PROCESSING_TIME:
throw new IllegalArgumentException(
String.format(
"%s is not permitted for user timers",
TimeDomain.SYNCHRONIZED_PROCESSING_TIME.name()));
default:
throw new IllegalArgumentException("Unknown time domain");
}
}
public static FunctionSpec translateDoFn(
DoFn<?, ?> fn,
TupleTag<?> tag,
Map<String, PCollectionView<?>> sideInputMapping,
DoFnSchemaInformation doFnSchemaInformation,
SdkComponents components) {
return FunctionSpec.newBuilder()
.setUrn(CUSTOM_JAVA_DO_FN_URN)
.setPayload(
ByteString.copyFrom(View on GitHub (pinned to 12126d8942)