prestodb/presto · error · PinotException

PINOT_UNEXPECTED_RESPONSE

PINOT_UNEXPECTED_RESPONSE

Error message

Empty routingTableEntries for %s. RoutingTable: %s

What it means

Thrown by getRoutingTableV1 when the controller's routing table response parses successfully but contains no routingTableEntries, i.e. Pinot cannot say which servers host the table's segments. The connector treats this as an unexpected controller response (PINOT_UNEXPECTED_RESPONSE).

Source

Thrown at presto-pinot-toolkit/src/main/java/com/facebook/presto/pinot/PinotClusterInfoFetcher.java:438

        catch (Exception e) {
            return getRoutingTableV1(tableName, responseBody);
        }
    }

    private Map<String, Map<String, List<String>>> getRoutingTableV1(String tableName, String responseBody)
    {
        ImmutableMap.Builder<String, Map<String, List<String>>> routingTableMap = ImmutableMap.builder();
        routingTablesJsonCodec.fromJson(responseBody).getRoutingTableSnapshot().forEach(snapshot -> {
            String tableNameWithType = snapshot.getTableName();
            // Response could contain info for tableName that matches the original table by prefix.
            // e.g. when table name is "table1", response could contain routingTable for "table1_staging"
            if (!tableName.equals(extractRawTableName(tableNameWithType))) {
                log.debug("Ignoring routingTable for %s", tableNameWithType);
            }
            else {
                List<Map<String, List<String>>> routingTableEntriesList = snapshot.getRoutingTableEntries();
                if (routingTableEntriesList.isEmpty()) {
                    throw new PinotException(
                            PINOT_UNEXPECTED_RESPONSE,
                            Optional.empty(),
                            String.format("Empty routingTableEntries for %s. RoutingTable: %s", tableName, responseBody));
                }

                // We are given multiple routing tables for a table, each with different segment to host assignments
                // We pick one randomly, so that a retry may hit a different server
                Map<String, List<String>> routingTableEntries = routingTableEntriesList.get(new Random().nextInt(routingTableEntriesList.size()));
                ImmutableMap.Builder<String, List<String>> routingTableBuilder = ImmutableMap.builder();
                routingTableEntries.forEach((host, segments) -> {
                    List<String> segmentsCopied = new ArrayList<>(segments);
                    Collections.shuffle(segmentsCopied);
                    routingTableBuilder.put(host, ImmutableList.copyOf(segmentsCopied));
                });
                routingTableMap.put(tableNameWithType, routingTableBuilder.build());
            }
        });
        return routingTableMap.build();

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Check in Pinot whether the table has segments loaded (curl /tables/<table>/segments)
  2. If the table was just created, wait for the batch/stream ingestion to load segments
  3. Compare the Pinot controller version against the connector's expectations and inspect the logged responseBody
  4. Drop and recreate/reload the table's segments if routing state is permanently empty

Example fix

// before
curl -X POST .../tables/myTable  # created table, no segments
// after
curl -X POST .../segments/myTable/upload  # load segments before querying
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the table has segments before routing
curl -s http://<controller>:9000/tables/<table>/segments | jq '.segments | length'

Try / catch

try {
    connector.listTables(...);
} catch (PinotException e) {
    if (e.getMessage().contains("Empty routingTableEntries")) {
        // treat table as not queryable; skip or alert
    }
    throw e;
}

Prevention

When it happens

Trigger: GET to the controller's routing table API returns a body whose snapshot has an empty routingTableEntries list for the requested table.

Common situations: Table exists but has no segments yet (just created, batch load not finished); table deleted while routing cache was being populated; Pinot controller returning degraded routing state during a rebalance; response body captured in the message shows the raw JSON.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/8a2bcc7884b3f2d2. Report an issue: GitHub.