cube-js/cube · error · Error
QuestDB custom granularity has an unparseable origin: ${orig
Error message
QuestDB custom granularity has an unparseable origin: ${origin} What it means
For custom granularities, QuestQuery.dateBinOriginShift parses the user-supplied origin timestamp with moment.utc to compute an offset from a fixed anchor. If the origin string cannot be parsed into a valid date, the driver throws instead of producing broken SQL.
Source
Thrown at packages/cubejs-questdb-driver/src/QuestQuery.ts:138
public dateBin(interval: string, source: string, origin: string): string {
const { stride, unit, count } = this.questFloorStride(interval);
// timestamp_floor(stride, ts, origin) only buckets forward from `origin`, so
// an origin later than the data collapses every row into a single bucket.
// Shift `origin` back by a whole number of strides (which preserves the bin
// phase, as flooring is periodic modulo the stride) to just before a fixed
// anchor that precedes any realistic data.
const shift = this.dateBinOriginShift(origin, unit, count);
const shiftedOrigin = shift > 0
? `dateadd('${unit}', ${-shift}, cast('${origin}' as timestamp))`
: `cast('${origin}' as timestamp)`;
return `timestamp_floor('${stride}', ${source}, ${shiftedOrigin})`;
}
private dateBinOriginShift(origin: string, unit: string, count: number): number {
const parsedOrigin = moment.utc(origin);
if (!parsedOrigin.isValid()) {
throw new Error(`QuestDB custom granularity has an unparseable origin: ${origin}`);
}
const anchor = moment.utc(DATE_BIN_ORIGIN_ANCHOR);
const strides = Math.ceil(parsedOrigin.diff(anchor, QUEST_UNIT_TO_MOMENT[unit]) / count);
const shift = strides > 0 ? strides * count : 0;
if (shift > INT32_MAX) {
throw new Error(
`QuestDB cannot anchor custom granularity '${count} ${unit}': origin shift ${shift} exceeds dateadd()'s 32-bit range`
);
}
return shift;
}
private questFloorStride(interval: string): { stride: string, unit: string, count: number } {
const [duration, type] = this.parseInterval(interval);View on GitHub (pinned to 7d981676b3)
Solutions
- Provide the origin as an ISO 8601 UTC string, e.g. '2024-01-01T00:00:00Z'
- Validate the origin value in your schema/config before compiling queries
- Remove the custom origin to fall back to the default anchor
Example fix
// before origin: 'January 1st, 2024' // after origin: '2024-01-01T00:00:00Z'
Defensive patterns
Strategy: validation
Validate before calling
if (!moment.utc(origin, moment.ISO_8601, true).isValid()) throw new Error(`origin ${origin} must be a valid ISO 8601 UTC date`); Type guard
function isValidIsoOrigin(s) { return typeof s === 'string' && moment.utc(s, moment.ISO_8601, true).isValid(); } Try / catch
try { await cube.query(...); } catch (e) { if (e.message.includes('unparseable origin')) { /* correct the origin in the data model and retry */ } throw e; } Prevention
- Always express custom-granularity origins as ISO 8601 UTC strings
- Validate data model config at load time
- Avoid locale-dependent date strings in schemas
When it happens
Trigger: Calling shift/dateBin with a custom-granularity origin that is not a valid UTC date string (empty string, wrong format, localized date text).
Common situations: A data model defining a custom granularity with an origin written in a non-ISO format (e.g. '01/02/2024' ambiguity aside, or 'Jan 2nd 2024' with locale issues); origin left undefined and stringified.
Related errors
- Can't parse date: '${from}'
- Can't parse date: '${to}'
- Can't parse date: '${dateString}'
- Cannot parse selector date range ${selector.dateRange}
- Granularity "${timeDimension.granularity}" not found in time
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/bbe5694a581400e3.
Report an issue: GitHub.