ToolJet/ToolJet · error · QueryBuilderError

"${operator}" requires at least one value

Error message

"${operator}" requires at least one value

What it means

Thrown for operator 'in' or 'not_in' when the resolved value list is empty. The value may be an array or a comma-separated string; after splitting/trimming, if zero elements remain, the IN()/NOT IN() list would be empty SQL, which is invalid, so the builder rejects it. Note: a bare empty string '' is NOT caught here (it splits to one element) — this primarily triggers on an empty array [].

Source

Thrown at plugins/packages/common/lib/queryBuilder.ts:329

    if (operator === 'is') {
      if (value === 'null') return `${col} IS NULL`;
      if (value === 'not_null') return `${col} IS NOT NULL`;
      throw new QueryBuilderError(`Unknown value for "is" operator: "${value}". Expected "null" or "not_null".`);
    }

    const sqlOp = OPERATORS[operator];
    if (!sqlOp) throw new QueryBuilderError(`Unknown operator: "${operator}"`);

    const effectiveOp = operator === 'ilike' && !this._dialect.supportsIlike() ? 'LIKE' : sqlOp;

    if (operator === 'in' || operator === 'not_in') {
      const values: unknown[] = Array.isArray(value)
        ? value
        : String(value)
            .split(',')
            .map((v) => v.trim());
      if ((values as unknown[]).length === 0) {
        throw new QueryBuilderError(`"${operator}" requires at least one value`);
      }
      const placeholders = (values as unknown[]).map((v) => this._addParam(v));
      return `${col} ${effectiveOp} (${placeholders.join(', ')})`;
    }

    if (operator === 'between') {
      if (!Array.isArray(value) || (value as unknown[]).length !== 2) {
        throw new QueryBuilderError('"between" requires value to be a 2-element array [from, to]');
      }
      return `${col} ${effectiveOp} ${this._addParam((value as unknown[])[0])} AND ${this._addParam(
        (value as unknown[])[1]
      )}`;
    }

    return `${col} ${effectiveOp} ${this._addParam(value)}`;
  }

  // ── SELECT clause builder ───────────────────────────────────────────────────

View on GitHub (pinned to 20602a8e10)

Solutions

  1. Guard multi-select inputs: if the array is empty, either omit the filter or substitute a sentinel.
  2. Pass at least one value (e.g. value: [selectedIds[0]]) when the list is required.
  3. Treat an empty 'in' list as 'no filter' on the caller side rather than sending it to the builder.

Example fix

// before
{ column: 'id', operator: 'in', value: [] }
// after: omit the filter when nothing is selected
const filters = selectedIds.length ? { id: { column: 'id', operator: 'in', value: selectedIds } } : {};
Defensive patterns

Strategy: validation

Validate before calling

function resolveInValues(value) {
  const arr = Array.isArray(value) ? value : String(value).split(',').map(v => v.trim());
  return arr.filter(v => v !== undefined && v !== null && v !== '');
}
// usage: if (f.operator === 'in' || f.operator === 'not_in') {
//   const vals = resolveInValues(f.value); if (!vals.length) delete filter; else f.value = vals;
// }

Type guard

function hasNonEmptyInValues(value: unknown): boolean {
  const arr = Array.isArray(value) ? value : String(value ?? '').split(',').map(v => v.trim());
  return arr.filter(v => v !== undefined && v !== null && v !== '').length > 0;
}

Try / catch

try {
  qb.listRows('t', { where_filters });
} catch (e) {
  if (e instanceof QueryBuilderError && /requires at least one value/.test(e.message)) {
    return { error: 'Select at least one value for the IN filter.' };
  }
  throw e;
}

Prevention

When it happens

Trigger: { operator: 'in', value: [] }, { operator: 'not_in', value: [] }, or a multi-select that returned no selection and was passed straight through as an array.

Common situations: Binding an 'in' filter to a multi-select checkbox list where the user deselected all options; an upstream service returning an empty allowed-values array; filtering by an empty tag/category set.

Related errors


AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13). Data as JSON: /api/errors/94779163f9f271a2. Report an issue: GitHub.