{"record":{"id":"c38eae5f675cacfc","repo":"apache/seatunnel","slug":"the-restored-committed-offset-checkpoint-does-not","errorCode":null,"errorMessage":"The restored committed-offset checkpoint does not contain its startup offset","messagePattern":"The restored committed-offset checkpoint does not contain its startup offset","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/enumerator/IncrementalSplitAssigner.java","lineNumber":293,"sourceCode":"                            split.getSplitStart(),\n                            split.getSplitEnd(),\n                            splitWatermark));\n        }\n        for (TableId tableId : capturedTables) {\n            Offset watermark = tableWatermarks.get(tableId);\n            if (minOffset == null || (watermark != null && watermark.isBefore(minOffset))) {\n                minOffset = watermark;\n                LOG.debug(\n                        \"Find the min offset {} of change log in table-watermarks {}\",\n                        watermark,\n                        tableId);\n            }\n        }\n        if (minOffset == null && startupOffset == null) {\n            if (restoredFromCheckpoint\n                    && sourceConfig.getStartupConfig().getStartupMode()\n                            == StartupMode.COMMITTED_OFFSET) {\n                throw new IllegalStateException(\n                        \"The restored committed-offset checkpoint does not contain its startup offset\");\n            }\n            startupOffset = sourceConfig.getStartupConfig().getStartupOffset(offsetFactory);\n        }\n        Offset incrementalSplitStartOffset = minOffset != null ? minOffset : startupOffset;\n        return new IncrementalSplit(\n                String.format(INCREMENTAL_SPLIT_ID, index),\n                capturedTables,\n                incrementalSplitStartOffset,\n                sourceConfig.getStopConfig().getStopOffset(offsetFactory),\n                completedSnapshotSplitInfos,\n                checkpointTables,\n                historyTableChanges);\n    }\n\n    @VisibleForTesting\n    void setSplitAssigned(boolean assigned) {\n        this.splitAssigned = assigned;","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/enumerator/IncrementalSplitAssigner.java#L275-L311","documentation":"IncrementalSplitAssigner.createIncrementalSplit computes the start offset for a new incremental split as the minimum low-watermark offset across finished snapshot splits, falling back to the startup offset. When a job started in COMMITTED_OFFSET mode is restored from checkpoint and neither a computed minOffset nor a stored startupOffset exists, the saved state cannot reproduce the committed-offset startup position, so IllegalStateException is thrown.","triggerScenarios":"Restoring a CDC job whose startup.mode = committed_offset from a checkpoint that lacks the startup offset in the assigner state and has no incremental/finished splits providing a minOffset.","commonSituations":"Checkpoint/savepoint from an older connector version that did not persist the committed-offset startup position, restoring a state file that lost the startup offset, or committed_offset mode combined with corrupted checkpoint state.","solutions":["Restart the job without restore (fresh run) so the committed offset is re-resolved from the database at startup","Change startup.mode (e.g. to 'initial' or 'earliest') so a startup offset can always be derived on restore","Use a checkpoint/savepoint created with a connector-cdc version that persists the startup offset","Verify checkpoint files are not truncated/corrupted; re-take a savepoint from a healthy running job"],"exampleFix":"// before\n\"startup.mode\" = \"committed_offset\"\n// restore from old checkpoint lacking startup offset -> IllegalStateException\n// after\n\"startup.mode\" = \"earliest\"   // startup offset always derivable on restore\n// or restart fresh with committed_offset\nbin/seatunnel.sh --config job.conf","handlingStrategy":"fallback","validationCode":"// before restore, confirm state carries a startup offset when mode is committed_offset\nif (startupMode == StartupMode.COMMITTED_OFFSET && state.getStartupOffset() == null) {\n    LOG.warn(\"Restored committed-offset state lacks startup offset; restart fresh instead of restoring\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    split = assigner.createIncrementalSplit(...);\n} catch (IllegalStateException e) {\n    // fall back: restart job fresh or with a different startup.mode\n}","preventionTips":["Prefer startup modes that always yield a derivable offset (initial/earliest) for restore-sensitive jobs","Only restore checkpoints from the same connector version that wrote them","Verify savepoint integrity before restoring committed_offset jobs"],"tags":["cdc","checkpoint","offset","restore","startup-mode"],"backgroundTag":"invalid-state-transition","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}