prestodb/presto · error · PinotException
PINOT_UNEXPECTED_RESPONSE
PINOT_UNEXPECTED_RESPONSE
Error message
Expected row of %d columns
What it means
PinotBrokerPageSource.setRows throws PinotException(PINOT_UNEXPECTED_RESPONSE) when a row in the Pinot broker's JSON response (the 'rows' array of a broker query result) is missing or has fewer fields than the query's column count (blockBuilders.size()). The connector expects the response's rows to align 1:1 with the requested columns; a short or null row means the broker returned a malformed or structurally unexpected response. The query text is attached via the exception's Optional.of(query) for debugging.
Source
Thrown at presto-pinot-toolkit/src/main/java/com/facebook/presto/pinot/PinotBrokerPageSource.java:265
pageBuilder.declarePositions(counter);
Page page = pageBuilder.build();
// TODO: Implement chunking if the result set is ginormous
finished = true;
return page;
}
finally {
readTimeNanos += System.nanoTime() - start;
}
}
protected void setRows(String query, List<BlockBuilder> blockBuilders, List<Type> types, JsonNode rows)
{
for (int rowNumber = 0; rowNumber < rows.size(); ++rowNumber) {
JsonNode result = rows.get(rowNumber);
if (result == null || result.size() < blockBuilders.size()) {
throw new PinotException(
PINOT_UNEXPECTED_RESPONSE,
Optional.of(query),
String.format("Expected row of %d columns", blockBuilders.size()));
}
for (int columnNumber = 0; columnNumber < blockBuilders.size(); columnNumber++) {
setValue(types.get(columnNumber), blockBuilders.get(columnNumber), result.get(columnNumber));
}
}
}
protected static void handleCommonResponse(String pinotQuery, JsonNode jsonBody)
{
JsonNode numServersResponded = jsonBody.get("numServersResponded");
JsonNode numServersQueried = jsonBody.get("numServersQueried");
if (numServersQueried == null || numServersResponded == null || numServersQueried.asInt() > numServersResponded.asInt()) {
throw new PinotException(
PINOT_INSUFFICIENT_SERVER_RESPONSE,View on GitHub (pinned to 55bb57d202)
Solutions
- Log/inspect the actual Pinot broker response (the failing query is in the PinotException's error message) to see the returned row shape
- Run the same query directly against the Pinot broker (POST /query/sql) to confirm whether the broker itself returns short rows
- Check Pinot broker health/version and network stability — retry the query; transient broker errors often produce malformed responses
- Upgrade the Pinot connector and Pinot cluster together so the JSON result format expectations match
- Reduce query complexity/column count or add LIMIT to rule out response-size related truncation
Example fix
// before (fragile direct query) SELECT * FROM pinot_table WHERE ts > 0 // after (explicit columns, bounded result) SELECT id, CAST(value AS DOUBLE) FROM pinot_table WHERE ts > 0 LIMIT 1000
Defensive patterns
Strategy: retry
Validate before calling
// Validate broker response shape before iterating rows
JsonNode rows = response.get("Rows");
if (rows == null || !rows.isArray()) {
throw new IllegalStateException("Pinot broker response missing Rows array");
}
for (JsonNode row : rows) {
if (row == null || row.size() < expectedColumnCount) {
throw new IllegalStateException("Pinot returned short row: " + row);
}
} Type guard
boolean isValidRows(JsonNode rows, int expectedColumns) {
return rows != null && rows.isArray()
&& java.util.stream.StreamSupport.stream(rows.spliterator(), false)
.allMatch(r -> r != null && r.size() >= expectedColumns);
} Try / catch
try {
return pageSource.getNextPage();
} catch (PinotException e) {
if (e.getErrorCode() == PINOT_UNEXPECTED_RESPONSE.toErrorCode()) {
if (attempt < MAX_RETRIES) { return retryQuery(); } // transient broker truncation
throw new IllegalStateException("Pinot broker returned malformed response for query: " + e.getMessage());
}
throw e;
} Prevention
- Retry idempotent Pinot queries once or twice — broker hiccups often produce truncated responses
- Keep Pinot cluster and connector versions matched to avoid JSON result-format drift
- Monitor broker error rates and GC/timeout metrics; malformed responses correlate with broker stress
- Run large queries directly against the broker endpoint to validate response shape before wiring into Presto
- Bound result sizes with LIMIT to reduce truncation risk on big scans
When it happens
Trigger: populateFromQueryResults iterates resultJson.get('Rows') and a row is null or result.size() < number of requested columns: Pinot returned an error/empty payload shaped differently than expected, a broker-side query failure produced a partial response, or the number of selected columns/aggregates changed while the response retained an older shape.
Common situations: Pinot broker under stress returning truncated responses; broker version incompatibility with the connector's expected JSON result format (e.g. pagination or aggregation result format change); queries mixing selection and aggregation where the connector builds column expectations differently from the server; large responses cut off by broker timeouts.
Related errors
- PINOT_UNSUPPORTED_COLUMN_TYPE
- PINOT_HTTP_ERROR
- INVALID_TABLE_PROPERTY
- ACCUMULO_TABLE_EXISTS
- NOT_SUPPORTED
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/726d1d91a39b62fa.
Report an issue: GitHub.