{"record":{"id":"c3e94b50d48ef599","repo":"makeplane/plane","slug":"invalid-x-axis-value-x-axis","errorCode":null,"errorMessage":"Invalid x_axis value: {x_axis}","messagePattern":"Invalid x_axis value: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"apps/api/plane/utils/analytics_plot.py","lineNumber":55,"sourceCode":"    \"completed_at\",\n]\n\nVALID_YAXIS = [\"issue_count\", \"estimate\"]\n\n\ndef annotate_with_monthly_dimension(queryset, field_name, attribute):\n    # Get the year and the months\n    year = ExtractYear(field_name)\n    month = ExtractMonth(field_name)\n    # Concat the year and month\n    dimension = Concat(year, Value(\"-\"), month, output_field=CharField())\n    # Annotate the dimension\n    return queryset.annotate(**{attribute: dimension})\n\n\ndef extract_axis(queryset, x_axis):\n    if x_axis not in VALID_ANALYTICS_FIELDS:\n        raise ValueError(f\"Invalid x_axis value: {x_axis}\")\n    # Format the dimension when the axis is in date\n    if x_axis in [\"created_at\", \"start_date\", \"target_date\", \"completed_at\"]:\n        queryset = annotate_with_monthly_dimension(queryset, x_axis, \"dimension\")\n        return queryset, \"dimension\"\n    else:\n        return queryset.annotate(dimension=F(x_axis)), \"dimension\"\n\n\ndef sort_data(data, temp_axis):\n    # When the axis is in priority order by\n    if temp_axis == \"priority\":\n        order = [\"low\", \"medium\", \"high\", \"urgent\", \"none\"]\n        return {key: data[key] for key in order if key in data}\n    else:\n        return dict(sorted(data.items(), key=lambda x: (x[0] == \"none\", x[0])))\n\n\ndef build_graph_plot(queryset, x_axis, y_axis, segment=None):","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/analytics_plot.py#L37-L73","documentation":"ValueError raised by `extract_axis` (analytics_plot.py:54) when `x_axis` is not in `VALID_ANALYTICS_FIELDS` - the lowercase allow-list: state_id, state__group, labels__id, assignees__id, estimate_point__value, issue_cycle__cycle_id, issue_module__module_id, priority, start_date, target_date, created_at, completed_at. Note this list uses different (lowercase) keys than build_chart.py's x_axis_mapper (UPPERCASE), a common source of confusion.","triggerScenarios":"Calling `extract_axis(queryset, x_axis)` directly, or hitting a code path that reaches it without prior validation, with an x_axis outside the 12-field allow-list (e.g. 'state', 'assignees', 'ESTATE', or a typo). The HTTP AnalyticsEndpoint validates first, so this fires mainly via internal callers like the bgtask analytic_plot_export.","commonSituations":"Backend bgtasks/tests calling extract_axis with the UPPERCASE keys from build_chart's x_axis_mapper; passing a related-field name like 'state' instead of 'state_id'; copy-paste from the chart API which expects different casing.","solutions":["Pass one of the 12 VALID_ANALYTICS_FIELDS values exactly (lowercase, with the trailing __id / __group where applicable).","If integrating with build_chart code that uses UPPERCASE keys, map them down to the lowercase analytics fields before calling extract_axis.","For the HTTP endpoint, prefer the endpoint's own 400 validation message; reserve direct extract_axis calls for validated inputs.","Add a constant import (`from plane.utils.analytics_plot import VALID_ANALYTICS_FIELDS`) and assert membership before calling."],"exampleFix":"# before\nextract_axis(qs, \"STATES\")      # not in lowercase list\nextract_axis(qs, \"state\")       # wrong - needs state_id\n\n# after\nextract_axis(qs, \"state_id\")","handlingStrategy":"validation","validationCode":"from plane.utils.analytics_plot import VALID_ANALYTICS_FIELDS\n\ndef is_valid_x_axis(x_axis: str) -> bool:\n    # mirrors analytics_plot.py:54 check\n    return x_axis in VALID_ANALYTICS_FIELDS","typeGuard":"from plane.utils.analytics_plot import VALID_ANALYTICS_FIELDS\n\ndef is_valid_analytics_field(value: str) -> bool:\n    \"\"\"Narrow a string to a known analytics x_axis/segment value.\"\"\"\n    return isinstance(value, str) and value in VALID_ANALYTICS_FIELDS","tryCatchPattern":null,"preventionTips":["Validate x_axis against VALID_ANALYTICS_FIELDS before calling extract_axis.","Remember these are lowercase field names; build_chart uses UPPERCASE labels.","Import the constant rather than re-hardcoding the list."],"tags":["analytics","validation","value-error","api"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}