apache/seatunnel · warning
POST response null for cursor
Error message
POST {} response null for cursor: {} What it means
EsRestClient.closeSqlCursor logs this when performRequest returns a null Response for the POST /_sql/close request that releases an SQL cursor. As with clearScroll's null branch, the client returned no response object, so the cursor could not be confirmed closed and the method returns false. The cursor should time out server-side eventually.
Solutions
- Verify Elasticsearch connectivity and any proxy in front of it that could swallow requests
- Check whether a custom RestClient wrapper returns null instead of throwing; fix the wrapper to surface transport exceptions
- Rely on cursor TTL: set a bounded cursor timeout so un-closed cursors expire server-side
- Retry the close once; closing an already-closed cursor is safe
- If persistent, upgrade the elasticsearch-rest-client dependency version
Example fix
// before: assume response exists
Response resp = restClient.performRequest(closeReq);
// after: bound cursor lifetime so cleanup is best-effort
// (set cursor timeout on the query) and retry close
if (!esClient.closeSqlCursor(cursor)) {
log.info("Cursor close unconfirmed; cursor will expire server-side");
} Defensive patterns
Strategy: fallback
Try / catch
// cursor close is advisory; rely on cursor TTL if unconfirmed
if (!esClient.closeSqlCursor(cursor)) {
log.info("SQL cursor close unconfirmed; cursor expires server-side");
} Prevention
- Set a bounded cursor timeout on SQL queries
- Check for wrappers that can return null from performRequest
- Close cursors while the REST client is still open
- Treat cursor cleanup as non-fatal; alert on recurrence instead
When it happens
Trigger: POST /_sql/close with body {"cursor": ...} returns null from performRequest — abnormal client/wrapper behavior rather than a normal HTTP error.
Common situations: Wrapped/shaded RestClient returning null on transport anomaly; test stubs; gateway dropping the request without a response.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- ADD_FIELD_FAILED
- CLEAR_INDEX_DATA_FAILED
- CREATE_INDEX_FAILED
- DELETE response null for scroll ID
- DROP_INDEX_FAILED
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/b7c8aa5ce06d3968.
Report an issue: GitHub.
Appendix: source
Thrown at seatunnel-connectors-v2/connector-elasticsearch/src/main/java/org/apache/seatunnel/connectors/seatunnel/elasticsearch/client/EsRestClient.java:369
* @param cursor The SQL cursor to close
* @return True if the cursor was successfully closed
*/
public boolean closeSqlCursor(String cursor) {
if (StringUtils.isEmpty(cursor)) {
log.debug("SQL cursor is empty; skip closing.");
return false;
}
String endpoint = "/_sql/close";
Request request = new Request("POST", endpoint);
Map<String, String> requestBody = new HashMap<>();
requestBody.put("cursor", cursor);
request.setJsonEntity(JsonUtils.toJsonString(requestBody));
try {
Response response = restClient.performRequest(request);
if (response == null) {
log.warn("POST {} response null for cursor: {}", endpoint, cursor);
return false;
}
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String entity = EntityUtils.toString(response.getEntity());
JsonNode jsonNode = JsonUtils.parseObject(entity);
return jsonNode.get("succeeded").asBoolean();
} else {
log.warn(
"POST {} response status code={} for cursor: {}",
endpoint,
response.getStatusLine().getStatusCode(),
cursor);
return false;
}
} catch (Exception ex) {
log.warn("Failed to close SQL cursor: {}", cursor, ex);
return false;
}View on GitHub (pinned to cf67b549a7)