apache/seatunnel · warning
Failed to close SQL cursor: {}
Error message
Failed to close SQL cursor: {} What it means
This is a WARN-level log (not a thrown exception) emitted by ElasticsearchSourceReader.scrollSearchResult when the reader finishes consuming a SQL SEARCH_AFTER-style cursor and the Elasticsearch REST client fails to close it via DELETE /_sql/close. The query results are already delivered; only server-side cursor state may linger until it expires. The cursor ID is logged to allow manual cleanup.
Source
Thrown at seatunnel-connectors-v2/connector-elasticsearch/src/main/java/org/apache/seatunnel/connectors/seatunnel/elasticsearch/source/ElasticsearchSourceReader.java:124
try {
ScrollResult scrollResult =
esRestClient.searchBySql(
sourceIndexInfo.getSqlQuery(), sourceIndexInfo.getScrollSize());
outputFromScrollResult(scrollResult, sourceIndexInfo, output, deserializer);
cursor = scrollResult.getScrollId();
while (StringUtils.isNotEmpty(cursor)) {
scrollResult =
esRestClient.searchWithSql(cursor, scrollResult.getColumnNodes());
outputFromScrollResult(scrollResult, sourceIndexInfo, output, deserializer);
cursor = scrollResult.getScrollId();
}
} finally {
if (StringUtils.isNotEmpty(cursor)) {
try {
esRestClient.closeSqlCursor(cursor);
} catch (Exception e) {
log.warn("Failed to close SQL cursor: {}", cursor, e);
}
}
}
} else {
// Check if we should use PIT API
if (SearchApiTypeEnum.PIT.equals(sourceIndexInfo.getSearchApiType())) {
log.info("Using Point-in-Time (PIT) API for index: {}", sourceIndexInfo.getIndex());
logSliceInfo(sourceIndexInfo);
searchWithPointInTime(sourceIndexInfo, output, deserializer);
} else {
log.info("Using Scroll API for index: {}", sourceIndexInfo.getIndex());
String scrollId = null;
try {
logSliceInfo(sourceIndexInfo);
ScrollResult scrollResult =
esRestClient.searchByScroll(
sourceIndexInfo.getIndex(),
sourceIndexInfo.getSource(),View on GitHub (pinned to cf67b549a7)
Solutions
- Check connectivity/auth between SeaTunnel and Elasticsearch; this warn usually accompanies a network or auth failure
- Increase the SQL cursor keep-alive (search.max_keep_alive / request-level keep_alive) if jobs idle between pages
- Verify the cursor was not already consumed/closed by a retry or duplicate poll; inspect the logged cursor ID with DELETE /_sql/close manually
- If recurring on your ES version, check SeaTunnel connector version for SQL cursor handling fixes
Example fix
// before
try {
esRestClient.closeSqlCursor(cursor);
} catch (Exception e) {
log.warn("Failed to close SQL cursor: {}", cursor, e);
}
// after
// add retry with short backoff before giving up
try {
esRestClient.closeSqlCursor(cursor);
} catch (Exception e) {
log.warn("Failed to close SQL cursor: {}, will rely on server keep-alive expiry", cursor, e);
} Defensive patterns
Strategy: try-catch
Validate before calling
// before submitting: verify ES reachable and auth OK curl -u user:pass -X GET "https://es-host:9200/_cluster/health" | jq '.status'
Try / catch
// this is a library-side warn; callers of the SeaTunnel source cannot catch it.
// operational guard: alert on the log message
// if (logs.match(/Failed to close SQL cursor/)) { clear cursor via DELETE /_sql/close } Prevention
- Set a generous SQL cursor keep_alive so cursors survive pagination gaps
- Monitor ES cluster health and network stability during long reads
- Alert on this log pattern and manually close the logged cursor ID if PIT/scroll contexts accumulate
When it happens
Trigger: pollNext -> scrollSearchResult completes a SQL cursor pagination loop, then esRestClient.closeSqlCursor(cursor) throws because the ES node is unreachable, the cursor already expired (search.max_keep_alive exceeded), was already closed, or the HTTP call fails mid-batch.
Common situations: Elasticsearch cluster restart or network blip between the last page fetch and cursor close; jobs that idle longer than the cursor keep-alive before the final close; query aborted upstream so the cursor was auto-cleaned; ES version mismatch where the _sql/close endpoint behaves differently.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Failed to clear scroll ID: ${scrollId}
- GET_ES_VERSION_FAILED
- DELETE_PIT_FAILED
- close elasticsearch connection error
- Error clearing scrollId
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/688e65493eeea5f1.
Report an issue: GitHub.