{"record":{"id":"e3cee10d41800105","repo":"makeplane/plane","slug":"invalid-group-by-field-group-by","errorCode":null,"errorMessage":"Invalid group_by field: {group_by}","messagePattern":"Invalid group_by field: (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"apps/api/plane/utils/build_chart.py","lineNumber":165,"sourceCode":"            \"count\": item[\"count\"],\n        }\n        for item in data\n    ]\n\n\ndef build_analytics_chart(\n    queryset: QuerySet[Issue],\n    x_axis: str,\n    group_by: Optional[str] = None,\n    date_filter: Optional[str] = None,\n) -> Dict[str, Union[List[Dict[str, Any]], Dict[str, str]]]:\n    # Validate x_axis\n    if x_axis not in x_axis_mapper:\n        raise ValidationError(f\"Invalid x_axis field: {x_axis}\")\n\n    # Validate group_by\n    if group_by and group_by not in x_axis_mapper:\n        raise ValidationError(f\"Invalid group_by field: {group_by}\")\n\n    field_mapping = get_x_axis_field()\n\n    id_field, name_field, additional_filter = field_mapping.get(x_axis, (None, None, {}))\n    group_field, group_name_field, group_additional_filter = field_mapping.get(group_by, (None, None, {}))\n\n    # Apply additional filters if they exist\n    if additional_filter or {}:\n        queryset = queryset.filter(**additional_filter)\n\n    if group_additional_filter or {}:\n        queryset = queryset.filter(**group_additional_filter)\n\n    aggregate_func = Count(\"id\", distinct=True)\n\n    if group_field:\n        response, schema = build_grouped_chart_response(\n            queryset,","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/build_chart.py#L147-L183","documentation":"Django ValidationError raised by `build_analytics_chart` (build_chart.py:165) when a non-None `group_by` is not a key of `x_axis_mapper`. group_by is optional (None is allowed and skips the check), but any supplied value must be one of the 13 UPPERCASE mapper keys. Note group_by is validated against the same x_axis_mapper as x_axis, not a separate grouping map.","triggerScenarios":"Calling build_analytics_chart with a group_by that is non-None and not in x_axis_mapper - e.g. 'assignees' (lowercase), 'ASSIGNEE' (wrong singular), or any analytics_plot lowercase field.","commonSituations":"Using a lowercase or singular form of a category ('label' vs 'LABELS'); forwarding user input without normalizing case; mixing analytics_plot field names into build_chart calls.","solutions":["Pass group_by as one of the 13 UPPERCASE x_axis_mapper keys, or None to skip grouping.","Normalize user-supplied group_by: `group_by = group_by.strip().upper() if group_by else None`.","Assert `group_by in x_axis_mapper` before the call when group_by is set.","Remember group_by and x_axis share the same valid set - there is no group-specific vocabulary."],"exampleFix":"# before\nbuild_analytics_chart(qs, x_axis=\"STATES\", group_by=\"assignees__id\")\n\n# after\nbuild_analytics_chart(qs, x_axis=\"STATES\", group_by=\"ASSIGNEES\")","handlingStrategy":"validation","validationCode":"from plane.utils.build_chart import x_axis_mapper\n\ndef is_valid_group_by(group_by) -> bool:\n    # group_by is optional and shares the same x_axis_mapper as x_axis\n    return group_by is None or group_by in x_axis_mapper","typeGuard":"from plane.utils.build_chart import x_axis_mapper\n\ndef is_valid_group_by(value) -> bool:\n    return value is None or (isinstance(value, str) and value in x_axis_mapper)","tryCatchPattern":"from django.core.exceptions import ValidationError\ntry:\n    build_analytics_chart(qs, x_axis, group_by)\nexcept ValidationError as e:\n    if 'Invalid group_by field' in str(e):\n        return bad_request(str(e))","preventionTips":["group_by uses the same UPPERCASE vocabulary as x_axis - there is no separate map.","Pass None to skip grouping.","Normalize user input with strip().upper() before validating."],"tags":["analytics","validation","chart","api"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}