{"record":{"id":"5972c837dd696538","repo":"makeplane/plane","slug":"empty-list-value","errorCode":"empty_list_value","errorMessage":"List value for '{key}' must not be empty","messagePattern":"List value for '(.+?)' must not be empty","errorType":"validation","errorClass":"DRFValidationError","httpStatus":400,"severity":"warning","filePath":"apps/api/plane/utils/filters/filter_backend.py","lineNumber":433,"sourceCode":"                {\n                    \"message\": \"Leaf filter must be a non-empty JSON object\",\n                    \"code\": \"invalid_leaf\",\n                }\n            )\n\n        for key, value in leaf.items():\n            if isinstance(key, str) and key.lower() in (\"or\", \"and\", \"not\"):\n                raise DRFValidationError(\n                    {\n                        \"message\": \"Logical operators cannot appear in a leaf filter object\",\n                        \"code\": \"operator_in_leaf\",\n                    }\n                )\n\n            # Lists/Tuples must contain only scalar values\n            if isinstance(value, (list, tuple)):\n                if len(value) == 0:\n                    raise DRFValidationError(\n                        {\n                            \"message\": f\"List value for '{key}' must not be empty\",\n                            \"code\": \"empty_list_value\",\n                        }\n                    )\n                for item in value:\n                    if not self._is_scalar(item):\n                        raise DRFValidationError(\n                            {\n                                \"message\": f\"List value for '{key}' must contain only scalar items\",\n                                \"code\": \"non_scalar_list_item\",\n                            }\n                        )\n                continue\n\n            # Scalars and None are allowed\n            if not self._is_scalar(value):\n                raise DRFValidationError(","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/filters/filter_backend.py#L415-L451","documentation":"Raised by _validate_leaf when a leaf field's value is a list or tuple of length zero. List values are allowed for filters like __in and __range, but an empty list would expand to a no-op or SQL `IN ()` which is invalid in most databases, so the backend rejects it before it reaches the ORM.","triggerScenarios":"GET .../?filters={\"state__in\":[]} ; GET .../?filters={\"sequence_id__range\":[]} ; client sends an empty selection from a multi-select picker.","commonSituations":"UI multi-select that submits even when nothing is chosen; programmatic filter that always emits an `__in` key but leaves it empty; clearing a picker without removing the filter clause.","solutions":["Drop the field key entirely when its list is empty — no filter is usually the intent.","On the client, filter out empty-list values before serializing: Object.fromEntries(Object.entries(f).filter(([,v]) => !(Array.isArray(v) && v.length === 0))).","If an empty IN is genuinely desired, replace it with an always-false condition explicitly rather than relying on empty-list semantics."],"exampleFix":"// before\n?filters={\"state__in\":[]}\n// after\n?filters={}  // omit the key, or send a non-empty list","handlingStrategy":"validation","validationCode":"def drop_empty_list_values(filter_data):\n    if isinstance(filter_data, dict):\n        return {k: (drop_empty_list_values(v) if isinstance(v, dict)\n                    else [drop_empty_list_values(c) for c in v] if isinstance(v, list) else v)\n                for k, v in filter_data.items()\n                if not (isinstance(v, (list, tuple)) and len(v) == 0)}\n    return filter_data","typeGuard":"function noEmptyListValues(node) {\n  if (typeof node !== 'object' || node === null) return true;\n  if (Array.isArray(node)) return node.every(noEmptyListValues);\n  return Object.entries(node).every(([k, v]) => !(Array.isArray(v) && v.length === 0))\n    && Object.values(node).every(noEmptyListValues);\n}","tryCatchPattern":null,"preventionTips":["Omit __in/__range keys when their list is empty rather than sending [].","In the UI picker, removing all selected items should also remove the filter clause."],"tags":["filters","schema","validation","api"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}