apache/seatunnel · critical · java.io.IOException
Invalid state: currentSplitId is null when finishing snapsho
Error message
Invalid state: currentSplitId is null when finishing snapshot split. emittedFinishedSplitId=%s, currentFetcher=%s, isFinished=%s
What it means
IncrementalSourceSplitReader throws this IOException from finishedSnapshotSplit when it is asked to finalize a snapshot split while its internal currentSplitId field is null, meaning no snapshot split is currently assigned to the reader. The reader tracks the active split id when a split is handed to it and uses it to report split completion; finalizing without an active split indicates the reader/split-enumerator state machine is out of sync, typically due to a checkpoint/restore or race in split assignment.
Source
Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/base/source/reader/IncrementalSourceSplitReader.java:207
public boolean canAssignNextSplit() {
return currentFetcher == null || currentFetcher.isFinished();
}
private boolean hasEmittedCurrentSplitFinished() {
return currentSplitId != null && currentSplitId.equals(emittedFinishedSplitId);
}
private RecordsWithSplitIds<SourceRecords> finishedSnapshotSplit() throws IOException {
final String splitId = currentSplitId;
if (splitId == null) {
log.warn(
"Invalid state: currentSplitId is null when finishing snapshot split. "
+ "emittedFinishedSplitId={}, currentFetcher={}, isFinished={}",
emittedFinishedSplitId,
currentFetcher != null ? currentFetcher.getClass().getSimpleName() : "null",
currentFetcher != null && currentFetcher.isFinished());
throw new IOException(
String.format(
"Invalid state: currentSplitId is null when finishing snapshot split. "
+ "emittedFinishedSplitId=%s, currentFetcher=%s, isFinished=%s",
emittedFinishedSplitId,
currentFetcher != null
? currentFetcher.getClass().getSimpleName()
: "null",
currentFetcher != null && currentFetcher.isFinished()));
}
if (splitId.equals(emittedFinishedSplitId)) {
return NoSplitRecords.INSTANCE;
}
emittedFinishedSplitId = splitId;
return ChangeEventRecords.forFinishedSplit(splitId);
}
private static final class NoSplitRecords implements RecordsWithSplitIds<SourceRecords> {
private static final NoSplitRecords INSTANCE = new NoSplitRecords();View on GitHub (pinned to cf67b549a7)
Solutions
- Restart the job from the last consistent checkpoint so split assignment state and fetcher state are restored together.
- Check that all source parallelism instances share a consistent checkpoint state (SnapshotSplitState / incremental source state serialization is intact).
- Upgrade SeaTunnel to a version with fixes for CDC split-assignment races in IncrementalSourceSplitReader.
- If reproducible, capture logs of emittedFinishedSplitId and currentFetcher and file an issue; the message includes both to diagnose which reader/fetcher desynced.
Example fix
// defensive: skip finalization when no split is assigned
if (currentSplitId == null) {
log.warn("Ignoring finished snapshot split signal with no assigned split");
return;
}
// before (throws)
throw new IOException(String.format("Invalid state: currentSplitId is null ...")); Defensive patterns
Strategy: retry
Validate before calling
// before relying on split completion, check assignment
if (reader.getCurrentSplitId() == null) {
log.warn("No split assigned; request reassignment from enumerator");
} Try / catch
try {
reader.fetch();
} catch (IOException e) {
if (e.getMessage().contains("currentSplitId is null")) {
// trigger checkpoint restore / reassignment instead of failing job
}
} Prevention
- Always run CDC jobs with checkpointing enabled so split state is restored atomically.
- Monitor for split-assignment gaps in logs after failover.
- Keep SeaTunnel CDC connectors updated for split-state race fixes.
When it happens
Trigger: fetch() detects the current fetcher has finished the snapshot split and calls finishedSnapshotSplit(), but currentSplitId is null because no split was assigned (or was already cleared after restore/failover) before completion was detected.
Common situations: Job recovery from a checkpoint where the split assignment was lost but the underlying Debezium fetcher still reports isFinished=true; a race where the enumerator reassigns splits while the old fetcher is finishing; bugs in custom split-assignment logic for CDC sources (MySQL/PostgreSQL CDC).
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- No split assigned
- Unsupported alter table event:
- Unsupported alter table event:
- DataTypeChanger not reset
- Unable to instantiate the database history class " + config.
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/d94b9d424c7bf69b.
Report an issue: GitHub.