{"record":{"id":"9cc44fb65ea43240","repo":"apache/beam","slug":"resolved-start-version-d-is-greater-than-resolved-end","errorCode":null,"errorMessage":"Resolved start version %d is greater than resolved end version %d","messagePattern":"Resolved start version (.+?) is greater than resolved end version (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/delta/src/main/java/org/apache/beam/sdk/io/delta/CreateCDCReadTasksDoFn.java","lineNumber":105,"sourceCode":"    } else if (startTimestamp != null) {\n      long startMillis = Instant.parse(startTimestamp).toEpochMilli();\n      resolvedStartVersion = tableImpl.getVersionAtOrAfterTimestamp(engine, startMillis);\n    } else {\n      throw new IllegalArgumentException(\"Starting version or timestamp must be specified.\");\n    }\n\n    long resolvedEndVersion;\n    if (endVersion != null) {\n      resolvedEndVersion = endVersion;\n    } else if (endTimestamp != null) {\n      long endMillis = Instant.parse(endTimestamp).toEpochMilli();\n      resolvedEndVersion = tableImpl.getVersionBeforeOrAtTimestamp(engine, endMillis);\n    } else {\n      resolvedEndVersion = table.getLatestSnapshot(engine).getVersion();\n    }\n\n    if (resolvedStartVersion > resolvedEndVersion) {\n      throw new IllegalArgumentException(\n          String.format(\n              \"Resolved start version %d is greater than resolved end version %d\",\n              resolvedStartVersion, resolvedEndVersion));\n    }\n\n    // 2. Load snapshot at resolvedEndVersion to get the scanStateRow\n    // We use endVersion's schema because it represents the latest schema in the\n    // read range\n    // which handles schema evolution (older files will just lack new columns).\n    Snapshot endSnapshot = table.getSnapshotAsOfVersion(engine, resolvedEndVersion);\n    Scan scan = endSnapshot.getScanBuilder().build();\n    Row scanState = scan.getScanState(engine);\n    SerializableRow serializableScanState = new SerializableRow(scanState);\n\n    // 3. Load snapshot at resolvedStartVersion to initialize the CommitRange\n    Snapshot startSnapshot = table.getSnapshotAsOfVersion(engine, resolvedStartVersion);\n\n    CommitRangeBuilder rangeBuilder =","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/delta/src/main/java/org/apache/beam/sdk/io/delta/CreateCDCReadTasksDoFn.java#L87-L123","documentation":"Thrown by CreateCDCReadTasksDoFn.processElement after resolving the table start and end versions (from timestamps or latest snapshot). Delta change-data-feed reads require a monotonically increasing version interval; if the resolved start version exceeds the resolved end version the requested time range is invalid and the read cannot proceed.","triggerScenarios":"Calling DeltaIO.readChanges with a start timestamp later than the end timestamp, or a start timestamp/version that resolves to a version after the table's latest snapshot (e.g. a future timestamp, or an end 'timestamp' of 0/epoch).","commonSituations":"Swapped start/end parameters; using local-clock timestamps against a table written with a different clock skew; asking for changes since a timestamp after the last commit; DST/timezone mistakes converting wall-clock time to epoch millis.","solutions":["Verify startTimestamp <= endTimestamp (and both are epoch millis in UTC) at pipeline-construction time before submitting the job.","Clamp the start timestamp to the table's earliest version, or use version-based reads instead of timestamps for deterministic bounds.","Check for timezone/DST conversion bugs in the code that produces the millis values (e.g. date parsed in local time vs UTC).","If the end timestamp is in the future, note Delta resolves it to the latest snapshot; ensure the start resolves to an earlier version or pick an earlier start."],"exampleFix":"// before\nLong start = parseLocal(\"2026-09-01\"); // timezone bug\nLong end = parseUtc(\"2026-08-01\");\nDeltaIO.read().readChanges().withStartTimestamp(start).withEndTimestamp(end);\n// after\nlong start = parseUtcInstant(\"2026-08-01\").toEpochMilli();\nlong end = parseUtcInstant(\"2026-09-01\").toEpochMilli();\nif (start > end) throw new IllegalArgumentException(\"start must be <= end\");\nDeltaIO.read().readChanges().withStartTimestamp(start).withEndTimestamp(end);","handlingStrategy":"validation","validationCode":"if (startMillis > endMillis) throw new IllegalArgumentException(\"start timestamp must be <= end timestamp (got \" + startMillis + \" > \" + endMillis + \")\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always convert timestamps to epoch millis in UTC; never use local-time parsers.","Assert start <= end in job-option validation before building the pipeline.","Prefer version-based reads when you need deterministic, order-independent bounds."],"tags":["java","delta-lake","cdc","argument-validation"],"backgroundTag":"value-out-of-range","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"}