makeplane/plane · error · ValueError

Invalid x_axis value: {x_axis}

Error message

Invalid x_axis value: {x_axis}

What it means

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.

Source

Thrown at apps/api/plane/utils/analytics_plot.py:55

    "completed_at",
]

VALID_YAXIS = ["issue_count", "estimate"]


def annotate_with_monthly_dimension(queryset, field_name, attribute):
    # Get the year and the months
    year = ExtractYear(field_name)
    month = ExtractMonth(field_name)
    # Concat the year and month
    dimension = Concat(year, Value("-"), month, output_field=CharField())
    # Annotate the dimension
    return queryset.annotate(**{attribute: dimension})


def extract_axis(queryset, x_axis):
    if x_axis not in VALID_ANALYTICS_FIELDS:
        raise ValueError(f"Invalid x_axis value: {x_axis}")
    # Format the dimension when the axis is in date
    if x_axis in ["created_at", "start_date", "target_date", "completed_at"]:
        queryset = annotate_with_monthly_dimension(queryset, x_axis, "dimension")
        return queryset, "dimension"
    else:
        return queryset.annotate(dimension=F(x_axis)), "dimension"


def sort_data(data, temp_axis):
    # When the axis is in priority order by
    if temp_axis == "priority":
        order = ["low", "medium", "high", "urgent", "none"]
        return {key: data[key] for key in order if key in data}
    else:
        return dict(sorted(data.items(), key=lambda x: (x[0] == "none", x[0])))


def build_graph_plot(queryset, x_axis, y_axis, segment=None):

View on GitHub (pinned to 1c8a60f858)

Solutions

  1. Pass one of the 12 VALID_ANALYTICS_FIELDS values exactly (lowercase, with the trailing __id / __group where applicable).
  2. If integrating with build_chart code that uses UPPERCASE keys, map them down to the lowercase analytics fields before calling extract_axis.
  3. For the HTTP endpoint, prefer the endpoint's own 400 validation message; reserve direct extract_axis calls for validated inputs.
  4. Add a constant import (`from plane.utils.analytics_plot import VALID_ANALYTICS_FIELDS`) and assert membership before calling.

Example fix

# before
extract_axis(qs, "STATES")      # not in lowercase list
extract_axis(qs, "state")       # wrong - needs state_id

# after
extract_axis(qs, "state_id")
Defensive patterns

Strategy: validation

Validate before calling

from plane.utils.analytics_plot import VALID_ANALYTICS_FIELDS

def is_valid_x_axis(x_axis: str) -> bool:
    # mirrors analytics_plot.py:54 check
    return x_axis in VALID_ANALYTICS_FIELDS

Type guard

from plane.utils.analytics_plot import VALID_ANALYTICS_FIELDS

def is_valid_analytics_field(value: str) -> bool:
    """Narrow a string to a known analytics x_axis/segment value."""
    return isinstance(value, str) and value in VALID_ANALYTICS_FIELDS

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/c3e94b50d48ef599. Report an issue: GitHub.