{"record":{"id":"5e752a51a062b8a5","repo":"cube-js/cube","slug":"your-cron-string-every-is-correct-but-we-s","errorCode":null,"errorMessage":"Your cron string ('${every}') is correct, but we support only equal time intervals.","messagePattern":"Your cron string \\('(.+?)'\\) is correct, but we support only equal time intervals\\.","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/BaseQuery.js","lineNumber":4887,"sourceCode":"        end: interval.next(),\n        dayOffset: dayOffset / 1000, // Convert from ms to seconds\n      };\n    } catch (err) {\n      throw new UserError(`Invalid cron string '${every}' in refreshKey (${err})`);\n    }\n  }\n\n  calcIntervalForCronString(refreshKey) {\n    const every = refreshKey.every || '1 hour';\n\n    const { start, end, dayOffset } = this.parseCronSyntax(every);\n\n    const interval = (end.getTime() - start.getTime()) / 1000;\n\n    if (\n      !/^(\\*|\\d+)? ?(\\*|\\d+) (\\*|\\d+) \\* \\* (\\*|\\d+)$/g.test(every.replace(/ +/g, ' ').replace(/^ | $/g, ''))\n    ) {\n      throw new UserError(`Your cron string ('${every}') is correct, but we support only equal time intervals.`);\n    }\n\n    let utcOffset = 0;\n\n    if (refreshKey.timezone || this.timezone) {\n      utcOffset = moment.tz(refreshKey.timezone).utcOffset() * 60;\n    }\n\n    return {\n      utcOffset,\n      interval,\n      dayOffset,\n    };\n  }\n\n  /**\n   * Both the rendered SQL and the descriptor handed to the orchestrator for local\n   * evaluation derive from this, so they cannot disagree on the formula.","sourceCodeStart":4869,"sourceCodeEnd":4905,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js#L4869-L4905","documentation":"Cube only supports cron strings that represent EQUAL time intervals (a restricted pattern like '*|d h *|m * * *|s'). The cron must parse correctly, but also match this restricted regex; otherwise this UserError is thrown even though the cron is technically valid.","triggerScenarios":"refreshKey with a valid cron expression whose fields are not a single equal interval, e.g. '0 0 1,15 * *' (two days a month) or '0 30 2 * *' combined with hour wildcards — anything failing the regex /^(\\*|\\d+)? ?(\\*|\\d+) (\\*|\\d+) \\* \\* (\\*|\\d+)$/ after whitespace normalization.","commonSituations":"Using cron lists/ranges (1,15 or 1-5) hoping for arbitrary schedules; a cron that is valid for standard cron but too complex for Cube's interval-based refresh; migrating an existing crontab entry directly.","solutions":["Simplify the cron to an equal interval pattern (one specific or wildcard value per field), e.g. '0 */4 * * *'","Prefer the simple interval form: every: '4 hours'","Split complex schedules into multiple pre-aggregations each with an equal-interval refreshKey"],"exampleFix":"// before\nrefreshKey: { every: '0 0 1,15 * *' }\n// after\nrefreshKey: { every: '1 day' }","handlingStrategy":"validation","validationCode":"const EQUAL_CRON = /^(\\*|\\d+)? ?(\\*|\\d+) (\\*|\\d+) \\* \\* (\\*|\\d+)$/;\nfunction validateEqualCron(every) {\n  const norm = every.replace(/ +/g, ' ').replace(/^ | $/g, '');\n  if (!EQUAL_CRON.test(norm)) throw new Error(`Cron must be an equal interval: ${every}`);\n}","typeGuard":"const isEqualIntervalCron = (s) => /^(\\*|\\d+)? ?(\\*|\\d+) (\\*|\\d+) \\* \\* (\\*|\\d+)$/.test(String(s).replace(/ +/g,' ').replace(/^ | $/g,''));","tryCatchPattern":"try { await cubeApi.query(q); } catch (e) { if (/support only equal time intervals/.test(e.message)) console.error('Simplify cron to an equal interval or use every: \"n unit\"'); throw e; }","preventionTips":["Avoid cron lists/ranges (commas, dashes) in refreshKey","Use 'every: \"n hours\"' form instead of cron for uniform intervals","Pre-aggregation refresh schedules must be periodic — model complex schedules with multiple pre-aggs"],"tags":["refresh-key","cron","validation"],"backgroundTag":"unsupported-cron-interval","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}