cube-js/cube · error

Date range expected to be in ${DEFAULT_TS_FORMAT} format but

Error message

Date range expected to be in ${DEFAULT_TS_FORMAT} format but ${range} found

What it means

The final check in checkDataRangeType requires each range endpoint to be a timestamp of length 23 ('yyyy-mm-dd hh:mm:ss.SSS') or 26 (with microseconds) — matching DEFAULT_TS_FORMAT. It throws when the strings parse as strings but have the wrong length/format, ensuring partition boundary math operates on uniform precision timestamps.

Source

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

    }
    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];
    if (from > to) {
      return null;
    }
    return [

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Format endpoints as 'yyyy-mm-dd hh:mm:ss.SSS' (23 chars) or with microseconds (26 chars), e.g. '2024-01-01 00:00:00.000'.
  2. Use a formatter that replaces 'T' with a space and trims/appends milliseconds instead of toISOString().
  3. Intersect ranges via PreAggregationPartitionRangeLoader.intersectDateRanges, which normalizes inputs, rather than hand-rolling.
  4. Check intermediate range-computation code for string slicing that alters length (e.g., cutting 'Z' but leaving 'T').

Example fix

// before
dateRange: [d1.toISOString(), d2.toISOString()] // '2024-01-01T00:00:00.000Z'
// after
dateRange: [d1.toISOString().slice(0, 23).replace('T', ' '), d2.toISOString().slice(0, 23).replace('T', ' ')]
Defensive patterns

Strategy: validation

Validate before calling

const TS_LEN = [23, 26];
function isTsFormat(range) {
  return isStringPairRange(range) &&
    range.every(s => TS_LEN.includes(s.length) && s[10] === ' ');
}
if (!isTsFormat(range)) throw new Error(`dateRange must be in 'yyyy-mm-dd hh:mm:ss.SSS' format`);

Try / catch

try {
  return await loader.loadPreAggregations();
} catch (e) {
  if (String(e.message).includes('format but')) {
    // reformat endpoints to 23/26-char space-separated timestamps and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing short-form dates like '2024-01-01' or '2024-01-01T00:00:00' (ISO 'T' separator, wrong length) into partition range loading; ranges from a different formatting helper producing 10- or 19-char strings.

Common situations: Dashboard code building ranges with toISOString() (24 chars, includes 'Z'/'T') instead of Cube's space-separated millisecond format; timezone-aware strings with offsets; legacy code passing 'YYYY-MM-DD'.

Related errors


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