pola-rs/polars · error · TypeError

invalid plan stage

Error message

invalid plan stage '{plan_stage}'

What it means

Sentinel for an unrecognized `plan_stage` argument in LazyFrame.show_graph. After engine selection and optimization-flag setup, the code branches on the known stages ('ir', 'physical', plus streaming/physical engine variants); a value outside that set falls through every branch, so the plan-visualization request is rejected as invalid.

Solutions

  1. Use plan_stage='ir' (optimized logical plan) or 'physical' (streaming physical plan)
  2. Omit plan_stage for the default
  3. Map legacy names: 'optimized' -> 'ir'

Example fix

// before
lf.show_graph(plan_stage='optimized')

// after
lf.show_graph(plan_stage='ir')
Defensive patterns

Strategy: validation

Validate before calling

VALID_STAGES = {'ir', 'physical'}
assert plan_stage in VALID_STAGES, f"plan_stage must be one of {VALID_STAGES}"

Type guard

def is_valid_plan_stage(stage) -> bool:
    return stage in ('ir', 'physical')

Try / catch

try:
    lf.show_graph(plan_stage=stage)
except TypeError as e:
    if "invalid plan stage" in str(e):
        lf.show_graph(plan_stage='ir')
    else:
        raise

Prevention

When it happens

Trigger: `lf.show_graph(plan_stage='optimized')`, `plan_stage='logical'`, typos like 'phys', or variables carrying stale stage names from older polars versions.

Common situations: Snippets from old tutorials (stage names changed across polars versions); typos; generic plan-visualization code parameterized with invalid values.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/79c8524273c4940f. Report an issue: GitHub.

Appendix: source

Thrown at py-polars/src/polars/lazyframe/frame.py:1527

        ...     "a"
        ... ).show_graph()  # doctest: +SKIP
        """
        engine_ = _select_engine(engine)

        optimizations = optimizations.__copy__()
        optimizations._pyoptflags.streaming = engine_.plan_engine == "streaming"
        _ldf = self._ldf.with_optimizations(optimizations._pyoptflags)

        if plan_stage == "ir":
            dot = _ldf.to_dot(optimized)
        elif plan_stage == "physical":
            if engine_.plan_engine == "streaming" or engine_.plan_engine == "auto":
                dot = _ldf.to_dot_streaming_phys(optimized)
            else:
                dot = _ldf.to_dot(optimized)
        else:
            error_msg = f"invalid plan stage '{plan_stage}'"
            raise TypeError(error_msg)

        return display_dot_graph(
            dot=dot,
            show=show,
            output_path=output_path,
            raw_output=raw_output,
            figsize=figsize,
        )

    def inspect(self, fmt: str = "{}") -> LazyFrame:
        """
        Inspect a node in the computation graph.

        Print the value that this node in the computation graph evaluates to and pass on
        the value.

        .. engine-support:: in-memory

View on GitHub (pinned to fe841f959e)