apache/seatunnel · error · EnumeratorClosedException
BigtableSourceSplitEnumerator already closed; cannot create
Error message
BigtableSourceSplitEnumerator already closed; cannot create client
What it means
BigtableSourceSplitEnumerator.getBigtableClient() checks the closed flag under stateLock before creating a BigtableClient and throws EnumeratorClosedException if the enumerator has already been closed. This prevents constructing a new gRPC client for a stopped split enumeration (e.g. after job cancel/failover), which would leak resources.
Source
Thrown at seatunnel-connectors-v2/connector-google-bigtable/src/main/java/org/apache/seatunnel/connectors/seatunnel/bigtable/source/BigtableSourceSplitEnumerator.java:423
/**
* Returns the shared client, creating it outside {@link #stateLock} when needed.
*
* <p>Construction is unlocked so a slow gRPC channel / credential load does not stall
* checkpoint snapshotting. Before publishing the new instance, this method re-checks under the
* lock: if another thread already published a client, or if {@link #close()} has already set
* {@code closed}, the just-built instance is closed immediately and never leaked.
*
* @throws EnumeratorClosedException if the enumerator is already closed (or closes during
* construction), so callers can distinguish shutdown from a real sampleRowKeys failure
*/
private BigtableClient getBigtableClient() {
synchronized (stateLock) {
if (bigtableClient != null) {
return bigtableClient;
}
if (closed) {
throw new EnumeratorClosedException(
"BigtableSourceSplitEnumerator already closed; cannot create client");
}
}
BigtableClient created = BigtableClient.createInstance(parameters);
synchronized (stateLock) {
if (closed) {
created.close();
throw new EnumeratorClosedException(
"BigtableSourceSplitEnumerator closed during client creation");
}
if (bigtableClient == null) {
bigtableClient = created;
return created;
}
created.close();
return bigtableClient;
}
}View on GitHub (pinned to cf67b549a7)
Solutions
- Retry/re-submit the job; this is usually a benign race — the new enumerator will recreate the client.
- If it recurs, check coordinator/worker logs for premature close() (e.g. checkpoint timeout or heartbeat loss) and increase relevant timeouts.
- Reduce split enumeration time (narrow the table/key range) so enumeration finishes before shutdown can race it.
Defensive patterns
Strategy: retry
Try / catch
try {
enumerator.open();
} catch (EnumeratorClosedException e) {
// enumerator was shut down concurrently; coordinator retry will recreate it
log.warn("Enumerator closed during split build, awaiting restart: {}", e.getMessage());
} Prevention
- Avoid cancelling jobs during initial split enumeration when possible.
- Ensure coordinator heartbeats/timeouts comfortably exceed enumeration duration.
- Narrow table scan ranges so enumeration completes quickly.
When it happens
Trigger: buildSplits() calls getBigtableClient() after the enumerator's close() ran — typically a race between split enumeration and job cancellation/failover, or a late openSplitEnumerator callback firing on a shut-down reader context.
Common situations: Cancelling a job while the enumerator is still enumerating splits; failover where a restored coordinator races the old instance's cleanup; slow first split enumeration on a large table hit by an early shutdown.
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
- BigtableSourceSplitEnumerator closed during client creation
- The split fetcher manager has closed.
- BUFFER_ADD_FAILED
- getProducedCatalogTables method has not been implemented.
- The Factory has not been implemented and the deprecated Plug
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/1e0571de27b4c918.
Report an issue: GitHub.