cube-js/cube · error

Date range expected to be a string array but ${range} found

Error message

Date range expected to be a string array but ${range} found

What it means

After checking array length, checkDataRangeType requires both range endpoints to be strings. It throws when either range[0] or range[1] is not a string (e.g., a Date object, number timestamp, or null). Partition range planning expects normalized string timestamps, so non-string ranges are rejected early with a descriptive message.

Source

Thrown at packages/cubejs-query-orchestrator/src/orchestrator/PreAggregationPartitionRangeLoader.ts:517

      return [dateRange[1], dateRange[1]];
    }
    if (!dateRange[1]) {
      return [dateRange[0], dateRange[0]];
    }
    return dateRange;
  }

  private static checkDataRangeType(range: QueryDateRange) {
    if (!range) {
      return;
    }

    if (range.length !== 2) {
      throw new Error(`Date range expected to be an array with 2 elements but ${range} found`);
    }

    if (typeof range[0] !== 'string' || typeof range[1] !== 'string') {
      throw new Error(`Date range expected to be a string array but ${range} found`);
    }

    if ((range[0].length !== 23 && range[0].length !== 26) || (range[1].length !== 23 && range[0].length !== 26)) {
      throw new Error(`Date range expected to be in ${DEFAULT_TS_FORMAT} format but ${range} found`);
    }
  }

  public static intersectDateRanges(rangeA: QueryDateRange | null, rangeB: QueryDateRange | null): QueryDateRange | null {
    PreAggregationPartitionRangeLoader.checkDataRangeType(rangeA);
    PreAggregationPartitionRangeLoader.checkDataRangeType(rangeB);
    if (!rangeB) {
      return rangeA;
    }
    if (!rangeA) {
      return rangeB;
    }
    const from = rangeA[0] > rangeB[0] ? rangeA[0] : rangeB[0];
    const to = rangeA[1] < rangeB[1] ? rangeA[1] : rangeB[1];

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Convert endpoints to ISO strings: [start.toISOString(), end.toISOString()].
  2. Validate both endpoints are typeof 'string' before calling the query/pre-aggregation API.
  3. If using the REST API, ensure dateRange is serialized as an array of two strings.
  4. Normalize the range through a helper that formats dates consistently (yyyy-mm-dd or full timestamp).

Example fix

// before
dateRange: [new Date('2024-01-01'), new Date('2024-02-01')]
// after
dateRange: [new Date('2024-01-01').toISOString(), new Date('2024-02-01').toISOString()]
Defensive patterns

Strategy: type-guard

Type guard

function isStringPairRange(r) {
  return Array.isArray(r) && r.length === 2 &&
    typeof r[0] === 'string' && typeof r[1] === 'string';
}
if (!isStringPairRange(range)) throw new Error('dateRange must be an array of two strings');

Try / catch

try {
  return await loader.loadPreAggregations();
} catch (e) {
  if (String(e.message).includes('expected to be a string array')) {
    // coerce Date/number endpoints to strings and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing JavaScript Date objects or numeric epochs in dateRange to a query handled by the partition range loader; a range where one endpoint is null/undefined; client code sending mixed types like ['2024-01-01', null].

Common situations: Using new Date() directly in dateRange instead of .toISOString(); serialization converting one endpoint to a number; template code filling in only one endpoint.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/9ead306378bffa60. Report an issue: GitHub.