{"record":{"id":"90b4f1b76afc2980","repo":"cube-js/cube","slug":"cannot-parse-selector-date-range-selector-datera","errorCode":null,"errorMessage":"Cannot parse selector date range ${selector.dateRange}","messagePattern":"Cannot parse selector date range (.+?)","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-api-gateway/src/gateway.ts","lineNumber":1119,"sourceCode":"\n  /**\n   * Post pre-aggregations build jobs entry point.\n   */\n  private async preAggregationsJobsPOST(\n    context: RequestContext,\n    selector: PreAggsSelector,\n  ): Promise<string[]> {\n    let jobs: string[] = [];\n\n    // There might be a few contexts but dateRange if present is still the same\n    // so let's normalize it only once.\n    // It's expected that selector.dateRange is provided in local time (without timezone)\n    // At the same time it is ok to get timestamps with `Z` (in UTC).\n    if (selector.dateRange) {\n      const start = parseUtcIntoLocalDate([{ val: selector.dateRange[0] }], 'UTC');\n      const end = parseUtcIntoLocalDate([{ val: selector.dateRange[1] }], 'UTC');\n      if (!start || !end) {\n        throw new UserError(`Cannot parse selector date range ${selector.dateRange}`);\n      }\n      selector.dateRange = [start, end];\n    }\n\n    const promise = Promise.all(\n      selector.contexts.map(async (config) => {\n        const ctx = <RequestContext>{\n          ...context,\n          ...config,\n        };\n        const _jobs = await this.postPreAggregationsBuildJobs(\n          ctx,\n          selector,\n        );\n        return _jobs;\n      })\n    );\n    const resolve = await promise;","sourceCodeStart":1101,"sourceCodeEnd":1137,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-api-gateway/src/gateway.ts#L1101-L1137","documentation":"When queueing pre-aggregation build jobs ('post' action), a `selector.dateRange` (array of exactly two date strings) is parsed via `parseUtcIntoLocalDate`. The API expects dates either in local time without a timezone or as UTC timestamps with a trailing 'Z'. If either endpoint of the range cannot be parsed into a valid date, this UserError is thrown and no jobs are queued.","triggerScenarios":"POST to `/cubejs-system/v1/pre-aggregations/jobs` with `action: 'post'` and `selector.dateRange` containing unparseable values: an empty string, `null`/`undefined` entries, non-ISO formats like '01/02/2024' or 'Feb 3 2024' if the parser rejects them, an array with fewer/more than the two accessed positions (`dateRange[0]`, `dateRange[1]`), or ISO strings with offsets like '+02:00' that the parser doesn't accept.","commonSituations":"Passing JavaScript `Date` objects serialized incorrectly (e.g., '[object Object]' or epoch numbers as strings); locale-formatted dates from a UI datepicker; time-zone-offset ISO strings ('2024-01-01T00:00:00+02:00') instead of 'Z'-suffixed UTC; accidentally passing a single date string instead of a two-element array.","solutions":["Send `dateRange` as a two-element array of strings: `[\"2024-01-01T00:00:00.000Z\", \"2024-02-01T00:00:00.000Z\"]` (UTC with 'Z') or plain local dates like `[\"2024-01-01\", \"2024-02-01\"]`.","Convert `Date` objects to ISO UTC strings before sending: `date.toISOString()`.","Ensure the array has exactly two elements in [start, end] order.","Avoid locale-dependent formats ('MM/DD/YYYY') and non-UTC timezone offsets; normalize to ISO 8601 first.","Log the raw `selector.dateRange` you're sending and validate both elements parse with `new Date(v)` and are not NaN before calling the API."],"exampleFix":"// before\n{ \"action\": \"post\", \"selector\": { \"dateRange\": [\"01/01/2024\", \"02/01/2024\"] } }\n\n// after\n{ \"action\": \"post\", \"selector\": { \"dateRange\": [\"2024-01-01T00:00:00.000Z\", \"2024-02-01T00:00:00.000Z\"] } }","handlingStrategy":"validation","validationCode":"function normalizeDateRange(range) {\n  if (!Array.isArray(range) || range.length !== 2) throw new Error('dateRange must be [start, end]');\n  const [start, end] = range.map(v => {\n    const d = v instanceof Date ? v : new Date(v);\n    if (isNaN(d.getTime())) throw new Error(`Unparseable date: ${v}`);\n    return d.toISOString(); // UTC with Z, accepted by the API\n  });\n  return [start, end];\n}","typeGuard":"function isParsedDateRange(r: unknown): r is [string, string] {\n  return Array.isArray(r) && r.length === 2 &&\n    r.every(v => typeof v === 'string' && !isNaN(Date.parse(v)));\n}","tryCatchPattern":"try {\n  const jobs = await postPreAggJobs({ action: 'post', selector: { ...selector, dateRange: normalizeDateRange(selector.dateRange) } });\n} catch (e) {\n  if (String(e.message).includes('Cannot parse selector date range')) {\n    console.error('Bad dateRange:', selector.dateRange); // convert to ISO-8601 UTC\n  }\n}","preventionTips":["Always send ISO 8601 strings (toISOString()) or plain 'YYYY-MM-DD' local dates — never Date objects, epochs, or locale formats.","Normalize all dates through a single helper before any API call.","Ensure the range has exactly two elements in [start, end] order.","Avoid timezone offsets like '+02:00'; use 'Z'-suffixed UTC."],"tags":["date-parsing","validation","pre-aggregations","rest-api"],"backgroundTag":"invalid-date-range-format","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}