{"record":{"id":"2292919a60327e39","repo":"apache/beam","slug":"trying-to-return-record-starting-at-d-which-is-before-the","errorCode":null,"errorMessage":"Trying to return record [starting at %d] which is before the start offset [%d]","messagePattern":"Trying to return record \\[starting at (.+?)\\] which is before the start offset \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/io/range/OffsetRangeTracker.java","lineNumber":92,"sourceCode":"  }\n\n  @Override\n  public synchronized Long getStopPosition() {\n    return stopOffset;\n  }\n\n  @Override\n  public boolean tryReturnRecordAt(boolean isAtSplitPoint, Long recordStart) {\n    return tryReturnRecordAt(isAtSplitPoint, recordStart.longValue());\n  }\n\n  public synchronized boolean tryReturnRecordAt(boolean isAtSplitPoint, long recordStart) {\n    if (!isStarted() && !isAtSplitPoint) {\n      throw new IllegalStateException(\n          String.format(\"The first record [starting at %d] must be at a split point\", recordStart));\n    }\n    if (recordStart < startOffset) {\n      throw new IllegalStateException(\n          String.format(\n              \"Trying to return record [starting at %d] which is before the start offset [%d]\",\n              recordStart, startOffset));\n    }\n    if (recordStart < lastRecordStart) {\n      throw new IllegalStateException(\n          String.format(\n              \"Trying to return record [starting at %d] \"\n                  + \"which is before the last-returned record [starting at %d]\",\n              recordStart, lastRecordStart));\n    }\n\n    if (lastRecordStart == -1) {\n      startOffset = recordStart;\n    }\n    lastRecordStart = recordStart;\n\n    if (isAtSplitPoint) {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/io/range/OffsetRangeTracker.java#L74-L110","documentation":"OffsetRangeTracker.tryReturnRecordAt rejects any record whose start offset is before the range's startOffset. The tracker represents the half-open range [startOffset, stopOffset); a record starting before startOffset belongs to a different (already completed or never-assigned) range, and returning it would cause duplicate or out-of-bounds processing, so it throws IllegalStateException.","triggerScenarios":"A reader calls tryReturnRecordAt with recordStart < startOffset — e.g. after resuming from a checkpoint the reader re-reads a record that began before the resumed offset, or split positions were computed on different boundaries than record boundaries.","commonSituations":"File/text sources that split at byte offsets but whose reader re-reads the last record overlapping the previous range and returns it without adjusting recordStart; miscomputed checkpoint offsets stored in CheckpointMarks; reading a shard with a stale/incorrect start offset after autoscaling resharding.","solutions":["Ensure the reader skips any record whose start is before startOffset and only claims records beginning at or after it.","Align split points with record boundaries so a claimed record's start always equals or exceeds startOffset.","Fix checkpoint persistence so the resumed startOffset matches the record boundary the reader actually resumes at."],"exampleFix":"// before\ntracker.tryReturnRecordAt(isAtSplitPoint, recordStart); // may be < startOffset\n\n// after\nif (recordStart >= tracker.startOffset()) {\n  tracker.tryReturnRecordAt(isAtSplitPoint, recordStart);\n} // else skip: record belongs to previous range","handlingStrategy":"validation","validationCode":"if (recordStart < tracker.startOffset()) { return true; /* skip record belonging to previous range */ }","typeGuard":null,"tryCatchPattern":"try {\n  tracker.tryReturnRecordAt(isAtSplitPoint, recordStart);\n} catch (IllegalStateException e) {\n  // drop the record or resynchronize the reader at startOffset\n}","preventionTips":["Skip records whose start offset precedes the tracker's startOffset instead of claiming them.","Persist checkpoints with record-boundary-accurate offsets.","Handle reshard/resume so the reader never re-emits records from before its assigned range."],"tags":["java","apache-beam","io","range-tracker","splittable"],"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"}