matplotlib/matplotlib · error · ValueError

Invalid value for 'layout': {layout!r}

Error message

Invalid value for 'layout': {layout!r}

What it means

set_layout_engine — reached from Figure(layout=...), plt.figure(layout=...), or fig.set_layout_engine(...) — accepts only the strings 'constrained', 'compressed', 'tight', 'none', None, or a LayoutEngine instance. Any other value falls through the dispatch chain to this ValueError (lib/matplotlib/figure.py:2825).

Source

Thrown at lib/matplotlib/figure.py:2825

        if layout == 'tight':
            new_layout_engine = TightLayoutEngine(**kwargs)
        elif layout == 'constrained':
            new_layout_engine = ConstrainedLayoutEngine(**kwargs)
        elif layout == 'compressed':
            new_layout_engine = ConstrainedLayoutEngine(compress=True,
                                                        **kwargs)
        elif layout == 'none':
            if self._layout_engine is not None:
                new_layout_engine = PlaceHolderLayoutEngine(
                    self._layout_engine.adjust_compatible,
                    self._layout_engine.colorbar_gridspec
                )
            else:
                new_layout_engine = None
        elif isinstance(layout, LayoutEngine):
            new_layout_engine = layout
        else:
            raise ValueError(f"Invalid value for 'layout': {layout!r}")

        if self._check_layout_engines_compat(self._layout_engine,
                                             new_layout_engine):
            self._layout_engine = new_layout_engine
        else:
            raise RuntimeError('Colorbar layout of new layout engine not '
                               'compatible with old engine, and a colorbar '
                               'has been created.  Engine not changed.')

    def get_layout_engine(self):
        return self._layout_engine

    # TODO: I'd like to dynamically add the _repr_html_ method
    # to the figure in the right context, but then IPython doesn't
    # use it, for some reason.

    def _repr_html_(self):
        # We can't use "isinstance" here, because then we'd end up importing

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Use one of the exact strings: 'constrained', 'compressed', 'tight', 'none'
  2. For custom engines pass an instance: fig.set_layout_engine(TightLayoutEngine())
  3. Normalize config values first: layout = str(layout).strip().lower() and validate against the allowed set

Example fix

// before
fig = plt.figure(layout='constrained ')

// after
layout = 'constrained'
assert layout in {'constrained', 'compressed', 'tight', 'none'}
fig = plt.figure(layout=layout)
Defensive patterns

Strategy: validation

Validate before calling

from matplotlib.layout_engine import LayoutEngine

ALLOWED_LAYOUTS = {'constrained', 'compressed', 'tight', 'none'}

def valid_layout(layout):
    return layout is None or layout in ALLOWED_LAYOUTS \
        or isinstance(layout, LayoutEngine)

Type guard

from matplotlib.layout_engine import LayoutEngine

def is_layout_engine(x):
    return isinstance(x, LayoutEngine)

Prevention

When it happens

Trigger: plt.figure(layout='constrained ') with a trailing space; layout='Tight' (wrong case); layout='auto' or 'default' (not real options); passing TightLayoutEngine (the class) instead of TightLayoutEngine().

Common situations: Typos and case errors in user-supplied config; forwarding unvalidated layout values from YAML/JSON settings; version drift where layout names got mangled between releases or docs.

Related errors


AI-assisted analysis of matplotlib/matplotlib@b379c1b69e (2026-08-21). Data as JSON: /api/errors/f9304ffc82077757. Report an issue: GitHub.