apache/beam · error
batch.groupIntoBatchesFn: unexpected timer family %q
Error message
batch.groupIntoBatchesFn: unexpected timer family %q
What it means
This panic fires in groupIntoBatchesFn.OnTimer when a timer callback arrives whose family string does not match the WindowEnd timer spec declared by the DoFn. The transform only schedules one timer family (fn.WindowEnd.Family) for lateness/flush handling, so any other family indicates misconfigured or foreign timers on the same key — an internal consistency check that panics loudly. Typical root causes are leftover timers from a previous pipeline version or a runner delivering timers from an unrelated spec.
Source
Thrown at sdks/go/pkg/beam/transforms/batch/batch.go:305
newBytes = cur
}
if fn.BatchSize > 0 && count >= fn.BatchSize {
fn.flush(sp, key, emit)
return
}
if fn.BatchSizeBytes > 0 && newBytes >= fn.BatchSizeBytes {
fn.flush(sp, key, emit)
return
}
}
func (fn *groupIntoBatchesFn) OnTimer(
ctx context.Context, ts beam.EventTime, sp state.Provider, tp timers.Provider,
key typex.T, timer timers.Context, emit func(typex.T, []typex.V),
) {
if timer.Family != fn.WindowEnd.Family {
panic(fmt.Sprintf("batch.groupIntoBatchesFn: unexpected timer family %q", timer.Family))
}
fn.codec.init(fn.ValueType.T)
fn.flush(sp, key, emit)
}
func (fn *groupIntoBatchesFn) flush(
sp state.Provider, key typex.T, emit func(typex.T, []typex.V),
) {
buf, ok, err := fn.Buffer.Read(sp)
if err != nil {
panic(err)
}
if !ok || len(buf) == 0 {
return
}
out := make([]typex.V, len(buf))
for i, b := range buf {View on GitHub (pinned to 12126d8942)
Solutions
- Check timer.Family in logs against fn.WindowEnd.Family; identify which spec scheduled the unexpected timer.
- After renaming the timer spec, drain or clear old timers (or use a fresh state/timer namespace) before redeploying.
- Make sure only groupIntoBatchesFn schedules timers on the same key/state namespace.
- If intentional multi-family timers are needed, extend OnTimer to switch on the family instead of panicking.
Example fix
// before
if timer.Family != fn.WindowEnd.Family {
panic(fmt.Sprintf("batch.groupIntoBatchesFn: unexpected timer family %q", timer.Family))
}
// after: ignore foreign timers instead of aborting the bundle
if timer.Family != fn.WindowEnd.Family {
return // not our timer; ignore
} Defensive patterns
Strategy: validation
Validate before calling
// Before deploying a renamed timer spec, assert no old-family timers remain:
if timer.Family != expectedFamily {
log.Printf("ignoring foreign timer family %q (expected %q)", timer.Family, expectedFamily)
return
} Type guard
func isWindowEndTimer(fn *groupIntoBatchesFn, t timers.Context) bool {
return t.Family == fn.WindowEnd.Family
} Try / catch
func safeOnTimer(fn *groupIntoBatchesFn, timer timers.Context) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("on-timer dispatch failed: %v", r)
}
}()
// call OnTimer
return nil
} Prevention
- Never rename timer families while reusing persistent state; drain old timers first.
- Only schedule timers for this DoFn on the given key namespace.
- Use a new state/timer namespace when changing timer specs.
- Switch on family in OnTimer if multiple families are ever expected.
When it happens
Trigger: OnTimer is invoked with timers.Context whose timer.Family differs from fn.WindowEnd.Family — e.g. after renaming the timer spec while old timers from a previous job version are still queued, or a misconfigured pipeline feeding this DoFn timers it never set.
Common situations: Developers hit this after modifying the timer family/identifier in an upgraded pipeline while reusing state (old timers still pending), or when a custom runner/test harness dispatches timers with a default family name.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- trigger.AfterCount(%v) must be a positive integer
- can't apply processing delay of less than a millisecond. Got
- can't apply an alignment period of less than a millisecond.
- GroupIntoBatches: %w
- GroupIntoBatchesWithShardedKey: %w
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/9c8629f3482dab5b.
Report an issue: GitHub.