cube-js/cube · error · UserError

Your cron string ('${every}') is correct, but we support onl

Error message

Your cron string ('${every}') is correct, but we support only equal time intervals.

What it means

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.

Source

Thrown at packages/cubejs-schema-compiler/src/adapter/BaseQuery.js:4887

        end: interval.next(),
        dayOffset: dayOffset / 1000, // Convert from ms to seconds
      };
    } catch (err) {
      throw new UserError(`Invalid cron string '${every}' in refreshKey (${err})`);
    }
  }

  calcIntervalForCronString(refreshKey) {
    const every = refreshKey.every || '1 hour';

    const { start, end, dayOffset } = this.parseCronSyntax(every);

    const interval = (end.getTime() - start.getTime()) / 1000;

    if (
      !/^(\*|\d+)? ?(\*|\d+) (\*|\d+) \* \* (\*|\d+)$/g.test(every.replace(/ +/g, ' ').replace(/^ | $/g, ''))
    ) {
      throw new UserError(`Your cron string ('${every}') is correct, but we support only equal time intervals.`);
    }

    let utcOffset = 0;

    if (refreshKey.timezone || this.timezone) {
      utcOffset = moment.tz(refreshKey.timezone).utcOffset() * 60;
    }

    return {
      utcOffset,
      interval,
      dayOffset,
    };
  }

  /**
   * Both the rendered SQL and the descriptor handed to the orchestrator for local
   * evaluation derive from this, so they cannot disagree on the formula.

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Simplify the cron to an equal interval pattern (one specific or wildcard value per field), e.g. '0 */4 * * *'
  2. Prefer the simple interval form: every: '4 hours'
  3. Split complex schedules into multiple pre-aggregations each with an equal-interval refreshKey

Example fix

// before
refreshKey: { every: '0 0 1,15 * *' }
// after
refreshKey: { every: '1 day' }
Defensive patterns

Strategy: validation

Validate before calling

const EQUAL_CRON = /^(\*|\d+)? ?(\*|\d+) (\*|\d+) \* \* (\*|\d+)$/;
function validateEqualCron(every) {
  const norm = every.replace(/ +/g, ' ').replace(/^ | $/g, '');
  if (!EQUAL_CRON.test(norm)) throw new Error(`Cron must be an equal interval: ${every}`);
}

Type guard

const isEqualIntervalCron = (s) => /^(\*|\d+)? ?(\*|\d+) (\*|\d+) \* \* (\*|\d+)$/.test(String(s).replace(/ +/g,' ').replace(/^ | $/g,''));

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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