{"record":{"id":"7ff8bbd8387cbb8f","repo":"cube-js/cube","slug":"can-t-parse-date-datestring","errorCode":null,"errorMessage":"Can't parse date: '${dateString}'","messagePattern":"Can't parse date: '(.+?)'","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-api-gateway/src/date-parser.js","lineNumber":96,"sourceCode":"    }\n\n    if (!Array.isArray(toResults) || !toResults.length) {\n      throw new UserError(`Can't parse date: '${to}'`);\n    }\n\n    const exactGranularity = ['second', 'minute', 'hour'].find(g => dateString.indexOf(g) !== -1) || 'day';\n    momentRange = [\n      momentFromResult(fromResults[0].start, timezone),\n      momentFromResult(toResults[0].start, timezone)\n    ];\n\n    momentRange = [momentRange[0].startOf(exactGranularity), momentRange[1].endOf(exactGranularity)];\n  } else {\n    const current = moment(now).tz(timezone);\n    const results = parse(dateString, new Date(current.format(moment.HTML5_FMT.DATETIME_LOCAL_MS)));\n\n    if (!results?.length) {\n      throw new UserError(`Can't parse date: '${dateString}'`);\n    }\n\n    const exactGranularity = ['second', 'minute', 'hour'].find(g => dateString.indexOf(g) !== -1) || 'day';\n    momentRange = results[0].end ? [\n      momentFromResult(results[0].start, timezone),\n      momentFromResult(results[0].end, timezone)\n    ] : [\n      momentFromResult(results[0].start, timezone),\n      momentFromResult(results[0].start, timezone)\n    ];\n    momentRange = [momentRange[0].startOf(exactGranularity), momentRange[1].endOf(exactGranularity)];\n  }\n\n  return momentRange.map(d => d.format(moment.HTML5_FMT.DATETIME_LOCAL_MS));\n}\n","sourceCodeStart":78,"sourceCodeEnd":112,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-api-gateway/src/date-parser.js#L78-L112","documentation":"When dateParser receives a dateRange string that matches none of the special patterns (this/last/next, today/yesterday/tomorrow, 'from ... to ...'), it falls back to chrono-node parsing of the whole string. If chrono returns no results the entire string is not a recognized date expression and Cube throws this UserError.","triggerScenarios":"Passing a single dateRange string chrono cannot parse, e.g. dateRange: ['31/12/2024'] in an ambiguous format, [''] (empty), ['2024'], ['next fortnight'], or a non-English expression like ['letzter monat'].","commonSituations":"Non-English locale date strings; day-first formats chrono misinterprets; empty strings from unset dashboard filters; strings built with toLocaleDateString(); users typing free-form ranges like 'last quarter to date' that match no pattern and no chrono result.","solutions":["Use an ISO 8601 string like '2024-12-31' or a ['start','end'] ISO array for dateRange — these are handled reliably","Use one of the supported relative expressions: 'today', 'yesterday', 'tomorrow', 'last month', 'last 30 days', 'this quarter'","Pre-validate the string with chrono-node (parse returns an empty array when it will fail) before sending the query","Convert locale-formatted dates to ISO in client code instead of relying on natural-language parsing"],"exampleFix":"// before\n{ dateRange: ['31/12/2024'] }\n// after\n{ dateRange: ['2024-12-31'] }","handlingStrategy":"validation","validationCode":"import { parse } from 'chrono-node';\nconst SUPPORTED = /^(this|last|next)\\s+(day|week|month|year|quarter|hour|minute|second)$|^(last|next)\\s+\\d+\\s+(day|week|month|year|quarter|hour|minute|second)$|^today$|^yesterday$|^tomorrow$/i;\nconst isValidDateRangeString = (s) => typeof s === 'string' && (SUPPORTED.test(s.trim()) || parse(s.trim()).length > 0);","typeGuard":"const isKnownRangeExpression = (s) => typeof s === 'string' && /^(this|last|next)\\s+(day|week|month|year|quarter|hour|minute|second)|(last|next)\\s+\\d+\\s+(day|week|month|year|quarter|hour|minute|second)|today|yesterday|tomorrow|from .+ to .+/i.test(s.trim());","tryCatchPattern":"try {\n  return await cube.load({ ...query, dateRange: [rangeString] });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Can't parse date:\")) {\n    return cube.load({ ...query, dateRange: [defaultStart, defaultEnd] });\n  }\n  throw e;\n}","preventionTips":["Use ISO strings or ISO arrays for dateRange — the natural-language parser (chrono-node) only understands English","Check strings against the supported patterns (today/yesterday/tomorrow, this|last|next unit, from X to Y) before sending","Convert locale dates to ISO in client code","Pre-run chrono.parse() on any dynamic range string"],"tags":["date-parsing","user-input","rest-api","validation"],"backgroundTag":"unparseable-date-string","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}