apache/beam · error · ValueError

No window for %s

Error message

No window for %s

What it means

MergingTriggerState.get_window looks up which window owns a given window_id by scanning window_ids and raises ValueError('No window for %s') when the id is not registered. This indicates the caller is using a stale or foreign window id that the state driver never assigned.

Source

Thrown at sdks/python/apache_beam/transforms/trigger.py:1191

  def merge(self, to_be_merged, merge_result):
    for window in to_be_merged:
      if window != merge_result:
        if window in self.window_ids:
          if merge_result in self.window_ids:
            merge_window_ids = self.window_ids[merge_result]
          else:
            merge_window_ids = self.window_ids[merge_result] = []
          merge_window_ids.extend(self.window_ids.pop(window))
          self._persist_window_ids()

  def known_windows(self):
    return list(self.window_ids)

  def get_window(self, window_id):
    for window, ids in self.window_ids.items():
      if window_id in ids:
        return window
    raise ValueError('No window for %s' % window_id)

  def _get_id(self, window):
    if window in self.window_ids:
      return self.window_ids[window][0]

    window_id = self._get_next_counter()
    self.window_ids[window] = [window_id]
    self._persist_window_ids()
    return window_id

  def _get_ids(self, window):
    return self.window_ids.get(window, [])

  def _get_next_counter(self):
    if not self.window_ids:
      self.counter = 0
    elif self.counter is None:
      self.counter = max(k for ids in self.window_ids.values() for k in ids)

View on GitHub (pinned to 12126d8942)

Solutions

  1. Obtain window ids only from the state driver (window_ids / _get_id) and never invent or cache them across cleanup.
  2. Clear any stored window ids when windows are output/garbage-collected; re-derive them for late data.
  3. Add a lookup guard before use: check the id exists in the driver's window_ids before calling APIs that resolve it.

Example fix

// before
state.get_state(window_id, tag)  # window_id from an old firing
// after
if window_id in [i for ids in state.window_ids.values() for i in ids]:
    state.get_state(window_id, tag)
Defensive patterns

Strategy: validation

Validate before calling

known_ids = {i for ids in state.window_ids.values() for i in ids}
assert window_id in known_ids, f'stale window_id {window_id}'

Type guard

def has_window_id(state, window_id):
    return any(window_id in ids for ids in state.window_ids.values())

Try / catch

try:
    window = state.get_window(window_id)
except ValueError:
    window = None  # window was cleaned up; skip stale callback

Prevention

When it happens

Trigger: Calling get_state/clear_state or trigger callbacks with a window_id that was never created via _get_id, or one already removed when windows were cleaned up after being fired/garbage-collected.

Common situations: Custom trigger drivers holding window ids across pane firings after the state was cleaned; timers firing for windows already garbage-collected; retaining ids from a previous bundle/retry.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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