apache/beam · error

size returned expected to be non-negative but received %v.

Error message

size returned expected to be non-negative but received %v.

What it means

In ProcessSizedElementsAndRestrictions.ProcessElement, the size function (sizeInv) invoked on the element and remaining restriction must return a non-negative size. A user-supplied Size() method returning a negative value is a contract violation, so the SDF aborts with this contextualized error.

Source

Thrown at sdks/go/pkg/beam/core/runtime/exec/sdf.go:214

	ws := elm.Elm2.(*FullValue).Elm2

	// If receiving directly from a datasource,
	// the element may not be wrapped in a *FullValue
	mainElm := convertIfNeeded(elm.Elm, &FullValue{})

	splitRests, err := n.splitInv.Invoke(ctx, mainElm, rest)
	if err != nil {
		return err
	}

	for _, splitRest := range splitRests {
		size, err := n.sizeInv.Invoke(ctx, mainElm, splitRest)
		if err != nil {
			return err
		}

		if size < 0 {
			err := errors.Errorf("size returned expected to be non-negative but received %v.", size)
			return errors.WithContextf(err, "%v", n)
		}
		output := &FullValue{}

		output.Timestamp = elm.Timestamp
		output.Windows = elm.Windows
		output.Elm = &FullValue{Elm: mainElm, Elm2: &FullValue{Elm: splitRest, Elm2: ws}}
		output.Elm2 = size

		if err := n.Out.ProcessElement(ctx, output, values...); err != nil {
			return err
		}
	}

	return nil
}

// FinishBundle resets the invokers.

View on GitHub (pinned to 12126d8942)

Solutions

  1. Fix the Size() method to return >= 0 for all valid element/restriction pairs
  2. Replace sentinel -1 returns with a proper default (e.g. 0 or an estimated size)
  3. Clamp the computed size: if size < 0 { size = 0 } (or fail fast with a descriptive error)
  4. Test Size() against edge-case elements (empty restrictions, zero-length payloads)

Example fix

// before
func (fn *myFn) Size(elm string, rest RangeRestriction) (float64, error) {
    return int64(len(rest.End) - len(rest.Start)) // can be negative
}
// after
func (fn *myFn) Size(elm string, rest RangeRestriction) (float64, error) {
    size := rest.End - rest.Start
    if size < 0 {
        return 0, fmt.Errorf("invalid restriction %v", rest)
    }
    return float64(size) * bytesPerElement, nil
}
Defensive patterns

Strategy: validation

Validate before calling

// Guard inside the Size() implementation
func (fn *myFn) Size(elm string, rest RangeRestriction) (float64, error) {
    size := rest.End - rest.Start
    if size < 0 {
        return 0, fmt.Errorf("invalid restriction %v: negative size", rest)
    }
    return float64(size), nil
}

Type guard

func nonNegativeSize(s float64) float64 { if s < 0 { return 0 }; return s }

Try / catch

if err := plan.Execute(ctx); err != nil {
    if strings.Contains(err.Error(), "non-negative") {
        log.Printf("Size() contract violated: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: A splittable DoFn whose Size() method (sizeFn) returns a negative value for a given element + restriction during ProcessElement — e.g. computing size from a byte length that underflowed or a lookup that returned -1 as a sentinel.

Common situations: Size() implementations using len() on data whose length computation underflows, returning -1 for 'unknown' size, or a size based on a restriction that is inverted/empty.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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