{"record":{"id":"590375f8874a3aa1","repo":"apache/beam","slug":"cannot-claim-work-after-restriction-tracker-returns-false","errorCode":null,"errorMessage":"cannot claim work after restriction tracker returns false","messagePattern":"cannot claim work after restriction tracker returns false","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/io/rtrackers/offsetrange/offsetrange.go","lineNumber":160,"sourceCode":"\t\tclaimed:   rest.Start - 1,\n\t\tattempted: -1,\n\t\tstopped:   false,\n\t\terr:       nil,\n\t}\n}\n\n// TryClaim accepts an int64 position representing the starting position of a block of work. It\n// successfully claims it if the position is greater than the previously claimed position and within\n// the restriction. Claiming a position at or beyond the end of the restriction signals that the\n// entire restriction has been processed and is now done, at which point this method signals to end\n// processing.\n//\n// The tracker stops with an error if a claim is attempted after the tracker has signalled to stop,\n// if a position is claimed before the start of the restriction, or if a position is claimed before\n// the latest successfully claimed.\nfunc (tracker *Tracker) TryClaim(rawPos any) bool {\n\tif tracker.stopped {\n\t\ttracker.err = errors.New(\"cannot claim work after restriction tracker returns false\")\n\t\treturn false\n\t}\n\n\tpos := rawPos.(int64)\n\ttracker.attempted = pos\n\tif pos < tracker.rest.Start {\n\t\ttracker.stopped = true\n\t\ttracker.err = errors.New(\"position claimed is out of bounds of the restriction\")\n\t\treturn false\n\t}\n\tif pos <= tracker.claimed {\n\t\ttracker.stopped = true\n\t\ttracker.err = errors.New(\"cannot claim a position lower than the previously claimed position\")\n\t\treturn false\n\t}\n\n\ttracker.claimed = pos\n\tif pos >= tracker.rest.End {","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/io/rtrackers/offsetrange/offsetrange.go#L142-L178","documentation":"The offsetrange.Tracker in Beam's Go SDK stops permanently once TryClaim returns false. This error is recorded when a claim is attempted on an already-stopped tracker, violating the restriction-tracker protocol which forbids claiming after a false return. The error is surfaced by the framework when the DoFn finishes.","triggerScenarios":"Calling TryClaim again after a previous TryClaim returned false — e.g. a ProcessElement loop that keeps iterating after the tracker signalled completion (pos >= rest.End), or user code re-claiming without checking the boolean return.","commonSituations":"for loops using a condition that doesn't re-check tracker.TryClaim's false result; claiming a position beyond the restriction end (which stops the tracker) and then attempting another claim in the same element; retry wrappers that re-invoke claim logic on failure.","solutions":["Check the boolean return of TryClaim and break the loop immediately when it returns false.","Use the rtrackers.RestrictiveTracker/TryClaim helper so claims are validated against the restriction and error paths handle stopping.","Restructure ProcessElement so that each claimed position ends the current element and the next claim happens in a fresh invocation."],"exampleFix":"// before\nfor pos := start; pos < end; pos++ {\n    tracker.TryClaim(pos) // keeps claiming after tracker stopped\n}\n// after\nfor pos := start; pos < end; pos++ {\n    if !tracker.TryClaim(pos) {\n        return tracker.GetError()\n    }\n}","handlingStrategy":"try-catch","validationCode":"if tracker.IsDone() || tracker.GetError() != nil {\n    return tracker.GetError() // do not attempt further claims\n}","typeGuard":"func canClaim(t restrictiontracker.RestrictionTracker, pos int64) bool {\n    return !t.IsDone() && t.GetError() == nil && t.TryClaim(pos)\n}","tryCatchPattern":"if !tracker.TryClaim(pos) {\n    return tracker.GetError() // stop; never claim again on this tracker\n}","preventionTips":["Always break out of claim loops the moment TryClaim returns false.","Never ignore TryClaim's boolean return value.","Prefer rtrackers.TryClaim helper which enforces the claim protocol.","Return tracker.GetError() from ProcessElement so the framework reports root causes."],"tags":["go","apache-beam","restriction-tracker","protocol-violation"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}