{"record":{"id":"0e2472dea1ec9941","repo":"makeplane/plane","slug":"filter-validation-errors-join-validation-er","errorCode":null,"errorMessage":"Filter validation errors: {'; '.join(validation_errors)}","messagePattern":"Filter validation errors: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"apps/api/plane/utils/filters/converters.py","lineNumber":396,"sourceCode":"                self._add_rich_filter(rich_filters, rich_field_name, \"in\", valid_values)\n\n            else:\n                # Handle single values\n                # Process date fields with helper method\n                if self._process_date_field(rich_field_name, [value], strict, validation_errors, rich_filters):\n                    continue\n\n                # For non-list values, use __exact operator for non-date fields\n                if self._validate_value(rich_field_name, value):\n                    self._add_rich_filter(rich_filters, rich_field_name, \"exact\", value)\n                else:\n                    error_msg = f\"Invalid value for {legacy_key}: {value}\"\n                    self._add_validation_error(strict, validation_errors, error_msg)\n\n        # Raise validation errors if in strict mode\n        if strict and validation_errors:\n            error_message = f\"Filter validation errors: {'; '.join(validation_errors)}\"\n            raise ValueError(error_message)\n\n        # Convert flat dict to rich filter format\n        return self._format_as_rich_filter(rich_filters)\n\n    def _format_as_rich_filter(self, flat_filters: Dict[str, Any]) -> Dict[str, Any]:\n        \"\"\"\n        Convert a flat dictionary of filters to the proper rich filter format.\n\n        Args:\n            flat_filters: Dictionary with field__lookup keys and values\n\n        Returns:\n            Rich filter format using logical operators (and/or/not)\n        \"\"\"\n        if not flat_filters:\n            return {}\n\n        # If only one filter, return as leaf node","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/filters/converters.py#L378-L414","documentation":"ValueError raised at the end of `convert` (converters.py:396) when `strict=True` and one or more per-field validation errors were collected during the loop (unsupported keys, invalid UUIDs, bad choices, invalid dates, etc.). All individual errors are joined with '; ' into a single message. It is the aggregate strict-mode failure for the whole legacy filter payload.","triggerScenarios":"Calling `converter.convert(legacy_filters, strict=True)` where any field fails its specific check: unsupported key (not in FIELD_MAPPINGS), invalid UUID, invalid choice value, no valid values in a list, invalid date, or an invalid single value. Each failure is appended to validation_errors; if non-empty at the end, this raises.","commonSituations":"Bulk-migrating legacy filter JSON with strict=True to surface all problems at once; client sending a filter payload with mixed valid/invalid keys; integrating an external tool whose filter vocabulary does not match FIELD_MAPPINGS; debug runs that enable strict to find data-quality issues.","solutions":["Read the joined message - each segment names the offending key and value; fix each one.","Cross-check keys against converter.FIELD_MAPPINGS and values against VALID_CHOICES / UUID_FIELDS / DATE_FIELDS.","Run with strict=False first to see which values are silently dropped, then correct them before enabling strict.","Validate the payload client-side against the same field/choice maps before sending."],"exampleFix":"# before\nlegacy = {\"state\": [\"bad-uuid\"], \"priority\": [\"extreme\"], \"target_date\": [\"foo\"]}\nconverter.convert(legacy, strict=True)  # raises aggregated errors\n\n# after - fix each value\nlegacy = {\n  \"state\": [\"<valid-state-uuid>\"],\n  \"priority\": [\"urgent\"],\n  \"target_date\": [\"2024-08-12\"],\n}\nconverter.convert(legacy, strict=True)","handlingStrategy":"validation","validationCode":"# Replay the converter's own strict checks before the real call\ndef preview_errors(converter, legacy_filters):\n    errors = []\n    try:\n        converter.convert(legacy_filters, strict=True)\n    except ValueError as e:\n        # message: 'Filter validation errors: <seg>; <seg>; ...'\n        body = str(e).split('Filter validation errors:', 1)[-1]\n        errors = [s.strip() for s in body.split(';') if s.strip()]\n    return errors","typeGuard":null,"tryCatchPattern":"try:\n    rich = converter.convert(legacy, strict=True)\nexcept ValueError as e:\n    # surface the per-field segments to the client for fixing\n    return bad_request({'errors': str(e).split('Filter validation errors:')[-1].split('; ')})","preventionTips":["Run convert with strict=False first to see which values get dropped, then fix them.","Cross-check keys against FIELD_MAPPINGS and values against VALID_CHOICES/UUID_FIELDS/DATE_FIELDS.","Validate the payload client-side against the same maps before sending.","Parse the joined message to enumerate every offending field."],"tags":["filters","validation","value-error","legacy-migration"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}