cube-js/cube · error · UserError

Invalid Job query format: ${error.message || error.toString(

Error message

Invalid Job query format: ${error.message || error.toString()}

What it means

This UserError is thrown by the pre-aggregations jobs endpoint (`POST /cubejs-system/v1/pre aggregations/jobs`) when the request body fails Joi validation against `preAggsJobsRequestSchema`. The body must contain a valid `action` ('post' or 'get') and, depending on the action, a valid `selector` (for 'post') or `tokens` (for 'get'). The Joi `error.message` is interpolated into the message so you can see exactly which field was invalid.

Source

Thrown at packages/cubejs-api-gateway/src/gateway.ts:1067

   * }
   * ```
   */
  private async preAggregationsJobs(req: Request, res: ExpressResponse) {
    const response = this.resToResultFn(res);
    const requestStarted = new Date();
    const context = <RequestContext>req.context;
    const query = <PreAggsJobsRequest>req.body;
    let result;
    try {
      await this.assertApiScope('jobs', req?.context?.securityContext);

      if (!query || Object.keys(query).length === 0) {
        throw new UserError('No job description provided');
      }

      const { error, value } = preAggsJobsRequestSchema.validate(query);
      if (error) {
        throw new UserError(`Invalid Job query format: ${error.message || error.toString()}`);
      }

      switch (query.action) {
        case 'post':
          result = await this.preAggregationsJobsPOST(
            context,
            <PreAggsSelector>value.selector
          );
          if (result.length === 0) {
            throw new UserError(
              'A user\'s selector doesn\'t match any of the ' +
              'pre-aggregations defined in the data model.'
            );
          }
          break;
        case 'get':
          result = await this.preAggregationsJobsGET(
            context,

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Read the Joi message appended after 'Invalid Job query format:' — it names the exact offending field and expected type.
  2. For build jobs, send `{"action": "post", "selector": {"contexts": [...], "dataSources": [...], "cubes": [...], "preAggregations": [...], "timezones": [...]}}`.
  3. For status polling, send `{"action": "get", "tokens": ["<jobToken>", ...]}` (optionally `"resType": "object"`).
  4. Ensure `action` is exactly lowercase 'post' or 'get' and that the body is a JSON object, not a string or array.
  5. Compare your payload against the schema version in your installed cubejs-api-gateway package, as fields have changed between releases.

Example fix

// before
curl -X POST /cubejs-system/v1/pre-aggregations/jobs -d '{"action":"rebuild","selector":{"cubes":["Events"]}}'

// after
curl -X POST /cubejs-system/v1/pre-aggregations/jobs -d '{"action":"post","selector":{"contexts":[{"dataSources":["default"]}],"cubes":["Events"],"timezones":["UTC"]}}'
Defensive patterns

Strategy: validation

Validate before calling

function validateJobsPayload(body) {
  if (!body || typeof body !== 'object') return 'body must be a JSON object';
  if (body.action !== 'post' && body.action !== 'get') return 'action must be "post" or "get"';
  if (body.action === 'post') {
    const s = body.selector;
    if (!s || typeof s !== 'object') return 'post requires a selector object';
    if (s.dateRange && (!Array.isArray(s.dateRange) || s.dateRange.length !== 2)) return 'dateRange must be [start, end]';
  }
  if (body.action === 'get' && (!Array.isArray(body.tokens) || body.tokens.some(t => typeof t !== 'string'))) {
    return 'get requires tokens: string[]';
  }
  return null;
}

Type guard

function isValidJobsRequest(q: unknown): q is { action: 'post' | 'get'; selector?: Record<string, unknown>; tokens?: string[] } {
  return !!q && typeof q === 'object' &&
    ['post', 'get'].includes((q as any).action);
}

Try / catch

try {
  const res = await fetch('/cubejs-system/v1/pre-aggregations/jobs', { method: 'POST', body: JSON.stringify(payload) });
  const data = await res.json();
  if (res.status !== 200) throw new Error(data.error || 'jobs request failed');
} catch (e) {
  if (String(e.message).includes('Invalid Job query format')) {
    console.error('Payload failed schema validation:', e.message); // fix the named field
  }
}

Prevention

When it happens

Trigger: POSTing to the pre-aggregations jobs API with a body that passes the empty-check but fails schema validation: missing/unknown `action` value, `action: 'post'` without a well-formed `selector` object (contexts, dataSources, cubes, preAggregations, timezones, dateRange of wrong types), or `action: 'get'` with missing/`tokens` not an array of strings, or unexpected extra fields disallowed by the schema.

Common situations: Hand-crafted curl/HTTP payloads for the pre-aggregations jobs API; automation scripts calling the jobs endpoint after a schema change or Cube version upgrade (the request schema has tightened over versions); typos like `action: 'Post'` or `selectors` instead of `selector`; passing the whole selector as a JSON string instead of an object.

Related errors


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