apache/druid · error · IllegalStateException (ISE)
Unable to compare timestamp for rows without a time column
Error message
Unable to compare timestamp for rows without a time column
What it means
getFirstEventTimestamp reads the __time column of a ScanResultValue's first event (list format, where events are Maps). If the event map has no __time entry, rows cannot be time-compared for stable ordering, so Druid throws an IllegalStateException.
Solutions
- Include __time in the query's columns list (in SQL, select __time or avoid excluding it) when using time-ordering.
- Remove the time-ordering if the time column is intentionally not part of results.
- If building ScanResultValues manually, always populate the __time key in each event map.
Example fix
// before
new ScanQueryBuilder().columns(Arrays.asList("col1")).order(Order.DESCENDING).build();
// after
new ScanQueryBuilder().columns(Arrays.asList(ColumnHolder.TIME_COLUMN_NAME, "col1")).order(Order.DESCENDING).build(); Defensive patterns
Strategy: validation
Validate before calling
if (query.getColumns() != null && !query.getColumns().contains(ColumnHolder.TIME_COLUMN_NAME) && query.getOrdering() != null) {
throw new IllegalArgumentException("time-ordered scans must include __time");
} Type guard
boolean firstEventHasTime(ScanResultValue sv) {
Object ev = ((List<?>) sv.getEvents()).get(0);
return ev instanceof Map && ((Map<?, ?>) ev).containsKey("__time");
} Try / catch
try {
long ts = sv.getFirstEventTimestamp(format);
} catch (IllegalStateException e) {
if (e.getMessage().contains("without a time column")) { /* add __time to columns and re-query */ }
else throw e;
} Prevention
- Always include __time in columns for time-ordered scans.
- In SQL, project __time or avoid excluding it.
- Never strip __time from ScanResultValues used in sorting.
When it happens
Trigger: stableLimitingSort comparing ScanResultValues whose first event (a Map) lacks ColumnHolder.TIME_COLUMN_NAME — e.g. scan queries that excluded __time from the columns list but requested descending time-ordering.
Common situations: SQL 'SELECT col1, col2 FROM t ORDER BY __time DESC' plans where __time is not projected; custom scans with explicit column lists omitting __time while ordering is enabled.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Cannot apply limit[ ] with offset[ ] due to overflow
- Cannot execute query with orderBy
- Cannot provide 'order' incompatible with 'orderBy'
- Column [ ] from 'orderBy' must also appear in 'columns'.
- Got a [ ] which isn't a
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/73f36363061b46ae.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/scan/ScanResultValue.java:103
public Object getEvents()
{
return events;
}
@Nullable
@JsonProperty
public RowSignature getRowSignature()
{
return rowSignature;
}
public long getFirstEventTimestamp(ScanQuery.ResultFormat resultFormat)
{
if (resultFormat.equals(ScanQuery.ResultFormat.RESULT_FORMAT_LIST)) {
Object timestampObj = ((Map<String, Object>) ((List<Object>) this.getEvents()).get(0)).get(ColumnHolder.TIME_COLUMN_NAME);
if (timestampObj == null) {
throw new ISE("Unable to compare timestamp for rows without a time column");
}
return DimensionHandlerUtils.convertObjectToLong(timestampObj);
} else if (resultFormat.equals(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)) {
int timeColumnIndex = this.getColumns().indexOf(ColumnHolder.TIME_COLUMN_NAME);
if (timeColumnIndex == -1) {
throw new ISE("Unable to compare timestamp for rows without a time column");
}
List<Object> firstEvent = (List<Object>) ((List<Object>) this.getEvents()).get(0);
return DimensionHandlerUtils.convertObjectToLong(firstEvent.get(timeColumnIndex));
}
throw new UOE("Unable to get first event timestamp using result format of [%s]", resultFormat.toString());
}
public List<ScanResultValue> toSingleEventScanResultValues()
{
List<ScanResultValue> singleEventScanResultValues = new ArrayList<>();
List<Object> events = (List<Object>) this.getEvents();
for (Object event : events) {View on GitHub (pinned to 9b90983fd2)