{"record":{"id":"056acc1cf5091208","repo":"apache/beam","slug":"cannot-set-both-endversion-and-endtimestamp","errorCode":null,"errorMessage":"Cannot set both endVersion and endTimestamp.","messagePattern":"Cannot set both endVersion and endTimestamp\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/delta/src/main/java/org/apache/beam/sdk/io/delta/DeltaIO.java","lineNumber":334,"sourceCode":"      return toBuilder().setHadoopConfig(config).build();\n    }\n\n    @Override\n    public PCollection<Row> expand(PBegin input) {\n      String path = getTablePath();\n      if (path == null) {\n        throw new IllegalArgumentException(\"Table path must be set.\");\n      }\n      if (getStartVersion() == null && getStartTimestamp() == null) {\n        // TODO: for unbounded reads, support using current HEAD or the latest snapshot\n        // as the default starting point.\n        throw new IllegalArgumentException(\"Either startVersion or startTimestamp must be set.\");\n      }\n      if (getStartVersion() != null && getStartTimestamp() != null) {\n        throw new IllegalArgumentException(\"Cannot set both startVersion and startTimestamp.\");\n      }\n      if (getEndVersion() != null && getEndTimestamp() != null) {\n        throw new IllegalArgumentException(\"Cannot set both endVersion and endTimestamp.\");\n      }\n\n      Configuration conf = new Configuration();\n      Map<String, String> hadoopConfig = getHadoopConfig();\n      if (hadoopConfig != null) {\n        for (Map.Entry<String, String> entry : hadoopConfig.entrySet()) {\n          conf.set(entry.getKey(), entry.getValue());\n        }\n      }\n      Engine engine = DefaultEngine.create(conf);\n      Table table = Table.forPath(engine, path);\n\n      TableImpl tableImpl = (TableImpl) table;\n\n      long resolvedEndVersion;\n      Long endVersionVal = getEndVersion();\n      String endTimestampVal = getEndTimestamp();\n      if (endVersionVal != null) {","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/delta/src/main/java/org/apache/beam/sdk/io/delta/DeltaIO.java#L316-L352","documentation":"expand() also enforces mutual exclusivity on the end bound: endVersion and endTimestamp cannot both be set. Each defines the end snapshot differently and combining them is ambiguous, so it throws IllegalArgumentException before reading.","triggerScenarios":"Calling both .withEndVersion(v) and .withEndTimestamp(t) on the DeltaIO.read() builder.","commonSituations":"Same merged-config or copy-paste mistakes as the start-bound error; a generic read-range UI exposing both fields; accidental chaining while experimenting with bounded vs unbounded reads.","solutions":["Remove .withEndTimestamp(...) and keep .withEndVersion(v)","Or remove .withEndVersion(...) and keep .withEndTimestamp(t)","Sanitize pipeline options so only one end-bound field is ever populated"],"exampleFix":"// before\nDeltaIO.read().withTable(path).withStartVersion(0L).withEndVersion(10L).withEndTimestamp(\"2024-06-01\");\n// after\nDeltaIO.read().withTable(path).withStartVersion(0L).withEndVersion(10L);","handlingStrategy":"validation","validationCode":"if (endVersion != null && endTimestamp != null) {\n  throw new IllegalArgumentException(\"Choose either endVersion or endTimestamp, not both\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Expose only one end-bound option or enforce exclusivity at config load","Leave both end bounds null for unbounded/streaming reads if supported","Keep start/end bound styles consistent (both versions or both timestamps)"],"tags":["java","apache-beam","delta-lake","configuration","mutually-exclusive"],"backgroundTag":"mutually-exclusive-options","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"}