apache/beam · error
Primary restriction %#v is not done. Check that the RTracker
Error message
Primary restriction %#v is not done. Check that the RTracker's TrySplit() at fraction 0.0 returns a completed primary restriction
What it means
During SDF checkpointing, TrySplit at fraction 0.0 is used to split off the entire remaining restriction; after a successful split the primary (current) restriction must be done. If the restriction tracker reports !IsDone() after such a split, the tracker violates the SDF contract and this error is raised.
Source
Thrown at sdks/go/pkg/beam/core/runtime/exec/sdf.go:717
return p, r, nil
}
// Checkpoint splits the remaining work in a restriction into residuals to be resumed
// later by the runner. This is done iff the underlying Splittable DoFn returns a resuming
// ProcessContinuation. If the split occurs and the primary restriction is marked as done
// my the RTracker, the Checkpoint fails as this is a potential data-loss case.
func (n *ProcessSizedElementsAndRestrictions) Checkpoint(ctx context.Context) ([]*FullValue, error) {
addContext := func(err error) error {
return errors.WithContext(err, "Attempting checkpoint in ProcessSizedElementsAndRestrictions")
}
_, r, err := n.Split(ctx, 0.0)
if err != nil {
return nil, addContext(err)
}
if !n.rt.IsDone() {
return nil, addContext(errors.Errorf("Primary restriction %#v is not done. Check that the RTracker's TrySplit() at fraction 0.0 returns a completed primary restriction", n.rt))
}
return r, nil
}
// singleWindowSplit is intended for splitting elements in non window-observing
// DoFns (or single-window elements in window-observing DoFns, since the
// behavior is identical). A single restriction split will occur and all windows
// present in the unsplit element will be present in both the resulting primary
// and residual.
func (n *ProcessSizedElementsAndRestrictions) singleWindowSplit(ctx context.Context, f float64, pWeState, rWeState any) ([]*FullValue, []*FullValue, error) {
if n.rt.IsDone() { // Not an error, but not splittable.
return []*FullValue{}, []*FullValue{}, nil
}
p, r, err := n.rt.TrySplit(f)
if err != nil {
return nil, nil, errView on GitHub (pinned to 12126d8942)
Solutions
- Fix the custom RestrictionTracker's TrySplit so fraction 0.0 returns a primary restriction for which IsDone() is true
- Verify IsDone() semantics: it must return true when the remaining restriction contains no work
- Add unit tests: TrySplit(0.0) then assert primary.IsDone()
- Use beam.TrySplit/RTrackerContracts test helpers to validate tracker behavior before deployment
Example fix
// before: fraction 0.0 keeps work in primary
func (t *myTracker) TrySplit(fraction float64) (primary, rest interface{}, ok bool) {
split := t.start + int64(fraction*float64(t.end-t.start))
return Range{t.start, split}, Range{split, t.end}, true
}
// after
func (t *myTracker) TrySplit(fraction float64) (primary, rest interface{}, ok bool) {
if fraction == 0.0 {
return Range{t.start, t.end}, nil, true // primary covers everything => done
}
split := t.start + int64(fraction*float64(t.end-t.start))
return Range{t.start, split}, Range{split, t.end}, true
} Defensive patterns
Strategy: validation
Validate before calling
// Contract self-check for custom trackers
func checkTracker(t beam.RestrictionTracker) error {
p, _, ok := t.TrySplit(0.0)
if !ok { return errors.New("split at 0.0 failed") }
_ = p
return nil // ensure primary would report IsDone()==true
} Try / catch
if err := plan.Execute(ctx); err != nil {
if strings.Contains(err.Error(), "Primary restriction") {
log.Printf("RestrictionTracker contract violation: %v", err)
}
return err
} Prevention
- Implement TrySplit(0.0) to return a fully-done primary restriction
- Keep IsDone() consistent with the tracker's remaining restriction
- Use Beam's restriction tracker test suites to validate custom trackers
- Add unit tests for split-at-zero and split-at-one boundaries
When it happens
Trigger: Calling Checkpoint() when the custom RestrictionTracker's TrySplit(0.0) returns a primary restriction that is not complete — i.e. a user-implemented TrySplit does not follow the contract that splitting at fraction 0.0 yields a fully-done primary.
Common situations: Custom RestrictionTracker implementations where TrySplit mishandles fraction 0.0 (returns a partial primary), off-by-one restriction arithmetic, or a tracker whose IsDone doesn't agree with its split behavior.
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
- DoFn terminated without fully processing restriction
- cannot claim work after restriction tracker returns false
- position claimed is out of bounds of the restriction
- cannot claim a position lower than the previously claimed po
- not all required SplittableDoFn methods are present. Missing
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/6672f74df16a3a3c.
Report an issue: GitHub.