apache/druid · error · IllegalArgumentException

When forcing limit push down, the provided limit spec must…

Error message

When forcing limit push down, the provided limit spec must have a limit.

What it means

With forceLimitPushDown=true, the provided DefaultLimitSpec must actually impose a limit (isLimited()); a spec with only sort orderings, or with Integer.MAX_VALUE limit, cannot be pushed down, so validateAndGetForceLimitPushDown throws IAE.

Solutions

  1. Set an actual numeric limit on the DefaultLimitSpec (e.g. new DefaultLimitSpec(orderings, someLimit)).
  2. Disable forceLimitPushDown when the query only sorts without limiting.
  3. Check DefaultLimitSpec.isLimited() before enabling the flag in programmatic query construction.

Example fix

// before
context.put("forceLimitPushDown", true);
query.withLimitSpec(new DefaultLimitSpec(orderings, Integer.MAX_VALUE));
// after
context.put("forceLimitPushDown", true);
query.withLimitSpec(new DefaultLimitSpec(orderings, 100)); // an actual limit
Defensive patterns

Strategy: validation

Validate before calling

boolean ok = !forcePushDown || (query.getLimitSpec() instanceof DefaultLimitSpec && ((DefaultLimitSpec) query.getLimitSpec()).isLimited());

Type guard

boolean limitable = (ls instanceof DefaultLimitSpec) && ((DefaultLimitSpec) ls).isLimited();

Try / catch

try { query.validateAndGetForceLimitPushDown(); } catch (IllegalArgumentException e) { /* set a real limit or remove the flag */ }

Prevention

When it happens

Trigger: forceLimitPushDown=true combined with a DefaultLimitSpec whose limit is not set/meaningful (e.g. limitSpec containing only orderBy columns, or limit <= 0 / MAX_VALUE per isLimited).

Common situations: Builders that use DefaultLimitSpec purely for sorting (order-by only); SQL queries with ORDER BY but no LIMIT compiled to an order-only limit spec while the context forces push down.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/7c12da4bb52aa14d. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java:510

            return rowOrdering.compare((ResultRow) lhs, (ResultRow) rhs);
          } else {
            //noinspection unchecked (Probably bySegment queries; see BySegmentQueryRunner for details)
            return ((Ordering) Comparators.naturalNullsFirst()).compare(lhs, rhs);
          }
        }
    );
  }

  private boolean validateAndGetForceLimitPushDown()
  {
    final boolean forcePushDown = context().getBoolean(GroupByQueryConfig.CTX_KEY_FORCE_LIMIT_PUSH_DOWN, false);
    if (forcePushDown) {
      if (!(limitSpec instanceof DefaultLimitSpec)) {
        throw new IAE("When forcing limit push down, a limit spec must be provided.");
      }

      if (!((DefaultLimitSpec) limitSpec).isLimited()) {
        throw new IAE("When forcing limit push down, the provided limit spec must have a limit.");
      }

      if (havingSpec != null) {
        throw new IAE("Cannot force limit push down when a having spec is present.");
      }

      for (OrderByColumnSpec orderBySpec : ((DefaultLimitSpec) limitSpec).getColumns()) {
        if (OrderByColumnSpec.getPostAggIndexForOrderBy(orderBySpec, postAggregatorSpecs) > -1) {
          throw new UnsupportedOperationException("Limit push down when sorting by a post aggregator is not supported.");
        }
      }
    }
    return forcePushDown;
  }

  private RowSignature computeResultRowSignature(final RowSignature.Finalization finalization)
  {
    final RowSignature.Builder builder = RowSignature.builder();

View on GitHub (pinned to 9b90983fd2)