apache/beam · error · Exception
Unexpected time domain
Error message
Unexpected time domain: %s
What it means
The streaming trigger driver's timer callback switches on time_domain and raises a bare Exception('Unexpected time domain: %s') for any domain other than WATERMARK (the only one its on-timer logic handles). REAL_TIME / DEPENDENT_REAL_TIME timers reaching this driver hit this fallback.
Solutions
- Only set TimeDomain.WATERMARK timers from trigger code, or handle real-time timers elsewhere (e.g. via the runner's timer API directly).
- If you need wall-clock behavior, encode it in the watermark hold or use a runner that supports real-time timers outside TriggerDriver.
- Check how the timer's time_domain was persisted; fix the code path that created a non-WATERMARK timer for this driver.
Example fix
// before state.set_timer(window, 'fire', TimeDomain.REAL_TIME, ts) # driver raises on fire // after state.set_timer(window, 'fire', TimeDomain.WATERMARK, ts)
Defensive patterns
Strategy: validation
Validate before calling
from apache_beam.transforms.timeutil import TimeDomain assert time_domain == TimeDomain.WATERMARK, 'TriggerDriver only supports WATERMARK timers'
Type guard
def is_watermark_timer(td):
from apache_beam.transforms.timeutil import TimeDomain
return td == TimeDomain.WATERMARK Try / catch
try:
driver.on_timer(...)
except Exception as e:
logger.error('Timer delivery failed: %s', e)
raise Prevention
- Set only TimeDomain.WATERMARK timers from trigger code.
- Handle real-time timer needs outside the built-in TriggerDriver.
- Re-check timer creation paths after Beam version upgrades.
When it happens
Trigger: A timer set with TimeDomain.REAL_TIME or TimeDomain.DEPENDENT_REAL_TIME fires and is delivered to this trigger driver's callback, which only implements the WATERMARK branch; or a corrupted/foreign time_domain value arrives from state serialization.
Common situations: Custom triggers or user code setting real-time timers that the built-in TriggerDriver does not support; Beam version drift where new time domains exist but this driver was not updated; tests firing timers with all domains.
Related errors
- combine_fn must be specified.
- count ( ) must be a positive integer.
- Invalid tag.
- key value is out of range
- Merging requested for non-mergeable state tag: %r.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/0c2f9c00eeaa72ef.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/python/apache_beam/transforms/trigger.py:1523
if self.is_merging:
state = MergeableStateAdapter(state)
window = state.get_window(window_id)
if state.get_state(window, self.TOMBSTONE):
return
if time_domain in (TimeDomain.WATERMARK, TimeDomain.REAL_TIME):
if not self.is_merging or window in state.known_windows():
context = state.at(window, self.clock)
if self.trigger_fn.should_fire(time_domain, timestamp, window, context):
finished = self.trigger_fn.on_fire(timestamp, window, context)
yield self._output(
window,
finished,
state,
timestamp,
time_domain == TimeDomain.WATERMARK)
else:
raise Exception('Unexpected time domain: %s' % time_domain)
def _output(self, window, finished, state, output_watermark, maybe_ontime):
"""Output window and clean up if appropriate."""
index = state.get_state(window, self.INDEX)
state.add_state(window, self.INDEX, 1)
if output_watermark <= window.max_timestamp():
nonspeculative_index = -1
timing = windowed_value.PaneInfoTiming.EARLY
if state.get_state(window, self.NONSPECULATIVE_INDEX):
nonspeculative_index = state.get_state(
window, self.NONSPECULATIVE_INDEX)
state.add_state(window, self.NONSPECULATIVE_INDEX, 1)
_LOGGER.warning(
'Watermark moved backwards in time '
'or late data moved window end forward.')
else:
nonspeculative_index = state.get_state(window, self.NONSPECULATIVE_INDEX)
state.add_state(window, self.NONSPECULATIVE_INDEX, 1)View on GitHub (pinned to 12126d8942)