{"record":{"id":"94779163f9f271a2","repo":"ToolJet/ToolJet","slug":"operator-requires-at-least-one-value","errorCode":null,"errorMessage":"\"${operator}\" requires at least one value","messagePattern":"\"(.+?)\" requires at least one value","errorType":"validation","errorClass":"QueryBuilderError","httpStatus":null,"severity":"error","filePath":"plugins/packages/common/lib/queryBuilder.ts","lineNumber":329,"sourceCode":"    if (operator === 'is') {\n      if (value === 'null') return `${col} IS NULL`;\n      if (value === 'not_null') return `${col} IS NOT NULL`;\n      throw new QueryBuilderError(`Unknown value for \"is\" operator: \"${value}\". Expected \"null\" or \"not_null\".`);\n    }\n\n    const sqlOp = OPERATORS[operator];\n    if (!sqlOp) throw new QueryBuilderError(`Unknown operator: \"${operator}\"`);\n\n    const effectiveOp = operator === 'ilike' && !this._dialect.supportsIlike() ? 'LIKE' : sqlOp;\n\n    if (operator === 'in' || operator === 'not_in') {\n      const values: unknown[] = Array.isArray(value)\n        ? value\n        : String(value)\n            .split(',')\n            .map((v) => v.trim());\n      if ((values as unknown[]).length === 0) {\n        throw new QueryBuilderError(`\"${operator}\" requires at least one value`);\n      }\n      const placeholders = (values as unknown[]).map((v) => this._addParam(v));\n      return `${col} ${effectiveOp} (${placeholders.join(', ')})`;\n    }\n\n    if (operator === 'between') {\n      if (!Array.isArray(value) || (value as unknown[]).length !== 2) {\n        throw new QueryBuilderError('\"between\" requires value to be a 2-element array [from, to]');\n      }\n      return `${col} ${effectiveOp} ${this._addParam((value as unknown[])[0])} AND ${this._addParam(\n        (value as unknown[])[1]\n      )}`;\n    }\n\n    return `${col} ${effectiveOp} ${this._addParam(value)}`;\n  }\n\n  // ── SELECT clause builder ───────────────────────────────────────────────────","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/ToolJet/ToolJet/blob/20602a8e101f2e59686c9afde0d1402aac2c8871/plugins/packages/common/lib/queryBuilder.ts#L311-L347","documentation":"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 [].","triggerScenarios":"{ operator: 'in', value: [] }, { operator: 'not_in', value: [] }, or a multi-select that returned no selection and was passed straight through as an array.","commonSituations":"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.","solutions":["Guard multi-select inputs: if the array is empty, either omit the filter or substitute a sentinel.","Pass at least one value (e.g. value: [selectedIds[0]]) when the list is required.","Treat an empty 'in' list as 'no filter' on the caller side rather than sending it to the builder."],"exampleFix":"// before\n{ column: 'id', operator: 'in', value: [] }\n// after: omit the filter when nothing is selected\nconst filters = selectedIds.length ? { id: { column: 'id', operator: 'in', value: selectedIds } } : {};","handlingStrategy":"validation","validationCode":"function resolveInValues(value) {\n  const arr = Array.isArray(value) ? value : String(value).split(',').map(v => v.trim());\n  return arr.filter(v => v !== undefined && v !== null && v !== '');\n}\n// usage: if (f.operator === 'in' || f.operator === 'not_in') {\n//   const vals = resolveInValues(f.value); if (!vals.length) delete filter; else f.value = vals;\n// }","typeGuard":"function hasNonEmptyInValues(value: unknown): boolean {\n  const arr = Array.isArray(value) ? value : String(value ?? '').split(',').map(v => v.trim());\n  return arr.filter(v => v !== undefined && v !== null && v !== '').length > 0;\n}","tryCatchPattern":"try {\n  qb.listRows('t', { where_filters });\n} catch (e) {\n  if (e instanceof QueryBuilderError && /requires at least one value/.test(e.message)) {\n    return { error: 'Select at least one value for the IN filter.' };\n  }\n  throw e;\n}","preventionTips":["Bind 'in' filters to a multi-select and omit the filter when nothing is chosen.","Default a multi-select to at least one option if the filter is mandatory.","Filter out empty values from the array before passing it."],"tags":["query-builder","where-filter","in-operator","validation"],"backgroundTag":null,"analyzedSha":"20602a8e101f2e59686c9afde0d1402aac2c8871","analyzedAt":"2026-08-13T05:58:54.221Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}