apache/beam · critical

zero length data for

Error message

zero length data for %v: 

What it means

PersistBundle processes each output datum's bytes and expects at least the windowed-value header. An empty data slice cannot contain any elements, so prism panics — a bundle sent data with zero length for an output, violating the element protocol.

Solutions

  1. Upgrade SDK and prism to matching Beam versions
  2. Inspect logs for the transform/output named in the panic to find the producing stage
  3. Report a bug with the bundle/transform details if the SDK legitimately emits empty data
  4. Retry the job to rule out transient transport corruption
Defensive patterns

Strategy: validation

Validate before calling

for _, d := range data { if len(d) == 0 { return errors.New("empty data blob in bundle output") } }

Try / catch

defer func() { if r := recover(); r != nil { log.Errorf("persist bundle panicked: %v", r) } }()

Prevention

When it happens

Trigger: In PersistBundle, a datum in the data list for an output has len(datum) == 0: the SDK/harness streamed an empty byte blob for an output that should carry encoded elements.

Common situations: Harness/SDK bug emitting empty data frames; network or transport layer truncation producing zero-length payloads; version skew in the element data framing protocol.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at sdks/go/pkg/beam/runners/prism/internal/engine/elementmanager.go:888

	// be scheduled when this stage's output watermark is held back. Only needed
	// once something self checkpoints, so pipelines that never do keep their
	// previous bundle scheduling exactly.
	if len(residuals.Data) > 0 {
		em.sawResidual.Store(true)
	}
	var changedConsumers set[string]
	if em.sawResidual.Load() {
		changedConsumers = set[string]{}
	}
	var seq int
	for output, data := range d.Raw {
		info := col2Coders[output]
		var newPending []element
		slog.Debug("PersistBundle: processing output", "bundle", rb, slog.String("output", output))
		for _, datum := range data {
			buf := bytes.NewBuffer(datum)
			if len(datum) == 0 {
				panic(fmt.Sprintf("zero length data for %v: ", output))
			}
			for {
				var rawBytes bytes.Buffer
				tee := io.TeeReader(buf, &rawBytes)
				ws, et, pn, err := exec.DecodeWindowedValueHeader(info.WDec, tee)
				if err != nil {
					if err == io.EOF {
						break
					}
					slog.Error("PersistBundle: error decoding watermarks", "error", err, "bundle", rb, slog.String("output", output))
					panic("error decoding watermarks")
				}
				if len(ws) == 0 {
					slog.Warn("PersistBundle: sdk provided a windowed value header 0 windows", "bundle", rb)
				}
				// TODO: Optimize unnecessary copies. This is doubleteeing.
				elmBytes := info.EDec(tee)
				var keyBytes []byte

View on GitHub (pinned to 12126d8942)