{"record":{"id":"6672f74df16a3a3c","repo":"apache/beam","slug":"primary-restriction-v-is-not-done-check-that-the-rtracker-s","errorCode":null,"errorMessage":"Primary restriction %#v is not done. Check that the RTracker's TrySplit() at fraction 0.0 returns a completed primary restriction","messagePattern":"Primary restriction %#v is not done\\. Check that the RTracker's TrySplit\\(\\) at fraction 0\\.0 returns a completed primary restriction","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/exec/sdf.go","lineNumber":717,"sourceCode":"\treturn p, r, nil\n}\n\n// Checkpoint splits the remaining work in a restriction into residuals to be resumed\n// later by the runner. This is done iff the underlying Splittable DoFn returns a resuming\n// ProcessContinuation. If the split occurs and the primary restriction is marked as done\n// my the RTracker, the Checkpoint fails as this is a potential data-loss case.\nfunc (n *ProcessSizedElementsAndRestrictions) Checkpoint(ctx context.Context) ([]*FullValue, error) {\n\taddContext := func(err error) error {\n\t\treturn errors.WithContext(err, \"Attempting checkpoint in ProcessSizedElementsAndRestrictions\")\n\t}\n\t_, r, err := n.Split(ctx, 0.0)\n\n\tif err != nil {\n\t\treturn nil, addContext(err)\n\t}\n\n\tif !n.rt.IsDone() {\n\t\treturn 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))\n\t}\n\n\treturn r, nil\n}\n\n// singleWindowSplit is intended for splitting elements in non window-observing\n// DoFns (or single-window elements in window-observing DoFns, since the\n// behavior is identical). A single restriction split will occur and all windows\n// present in the unsplit element will be present in both the resulting primary\n// and residual.\nfunc (n *ProcessSizedElementsAndRestrictions) singleWindowSplit(ctx context.Context, f float64, pWeState, rWeState any) ([]*FullValue, []*FullValue, error) {\n\tif n.rt.IsDone() { // Not an error, but not splittable.\n\t\treturn []*FullValue{}, []*FullValue{}, nil\n\t}\n\n\tp, r, err := n.rt.TrySplit(f)\n\tif err != nil {\n\t\treturn nil, nil, err","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/exec/sdf.go#L699-L735","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: fraction 0.0 keeps work in primary\nfunc (t *myTracker) TrySplit(fraction float64) (primary, rest interface{}, ok bool) {\n    split := t.start + int64(fraction*float64(t.end-t.start))\n    return Range{t.start, split}, Range{split, t.end}, true\n}\n// after\nfunc (t *myTracker) TrySplit(fraction float64) (primary, rest interface{}, ok bool) {\n    if fraction == 0.0 {\n        return Range{t.start, t.end}, nil, true // primary covers everything => done\n    }\n    split := t.start + int64(fraction*float64(t.end-t.start))\n    return Range{t.start, split}, Range{split, t.end}, true\n}","handlingStrategy":"validation","validationCode":"// Contract self-check for custom trackers\nfunc checkTracker(t beam.RestrictionTracker) error {\n    p, _, ok := t.TrySplit(0.0)\n    if !ok { return errors.New(\"split at 0.0 failed\") }\n    _ = p\n    return nil // ensure primary would report IsDone()==true\n}","typeGuard":null,"tryCatchPattern":"if err := plan.Execute(ctx); err != nil {\n    if strings.Contains(err.Error(), \"Primary restriction\") {\n        log.Printf(\"RestrictionTracker contract violation: %v\", err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["go","apache-beam","splittable-dofn","restriction-tracker"],"backgroundTag":"internal-invariant-violation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}