apache/beam · error

unsupported WindowCoder Type

Error message

unsupported WindowCoder Type: %v

What it means

When decoding timers, Prism inspects the timer's window coder and builds a window extractor for the supported coder types. If the WindowCoder's type is not one of the recognized kinds (global, interval, custom length-prefixed, etc.), the runner panics because it cannot know how to delimit or decode windows in timer payloads.

Solutions

  1. Use standard windowing (fixed/interval or global windows) in the timers' stage
  2. Upgrade prism/Beam so the window coder type is supported on both sides
  3. Ensure SDK harness and runner versions match to avoid coder type skew
  4. If a legitimate coder type is missing, report it to the Beam project with the coder bytes

Example fix

// before: custom window coder in a timers-using stage
w := window.NewCustomWindows(...)
// after: use a supported windowing strategy
w := window.NewFixedWindows(duration)
Defensive patterns

Strategy: type-guard

Validate before calling

switch winCoder.(type) {
case window.GlobalWindowCoder, window.IntervalWindowCoder:
    // supported
default:
    return fmt.Errorf("window coder %T unsupported for timers", winCoder)
}

Prevention

When it happens

Trigger: decodeTimerIter (invoked from triageTimers) encountering a timer whose window coder type is unrecognized by the switch — e.g. a new/exotic WindowCoder type from an SDK that prism hasn't implemented, or a coder mismatch.

Common situations: Cross-language pipelines (e.g. Python/Java SDK emitting a window coder prism's Go decoder doesn't know), custom windowing strategies, or Beam version skew where the SDK supports a coder the runner does not.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/runners/prism/internal/engine/timers.go:71

	case WinGlobal:
		singleWindowExtractor = func(*decoder) typex.Window {
			return window.GlobalWindow{}
		}
	case WinInterval:
		singleWindowExtractor = func(d *decoder) typex.Window {
			return d.IntervalWindow()
		}
	case WinCustom:
		// Default to a length prefixed window coder here until we have different information.
		// Everything else is either:: variable, 1,  4, or 8 bytes long
		// KVs (especially nested ones, could occur but are unlikely, and it would be
		// easier for Prism to force such coders to be length prefixed.
		singleWindowExtractor = func(d *decoder) typex.Window {
			return d.CustomWindowLengthPrefixed()
		}
	default:
		// Unsupported
		panic(fmt.Sprintf("unsupported WindowCoder Type: %v", winCoder))
	}

	return func(yield func(timerRet) bool) {
		for len(raw) > 0 {
			keyBytes := keyDec(bytes.NewBuffer(raw))
			d := decoder{raw: raw, cursor: len(keyBytes)}
			tag := string(d.Bytes())

			var ws []typex.Window
			numWin := d.Fixed32()
			for i := 0; i < int(numWin); i++ {
				ws = append(ws, singleWindowExtractor(&d))
			}

			clear := d.Bool()
			hold := mtime.MaxTimestamp
			if clear {
				var elms []element

View on GitHub (pinned to 12126d8942)