matplotlib/matplotlib · error · ValueError

Invalid vmin or vmax

Error message

Invalid vmin or vmax

What it means

For scale-backed norms (LogNorm, AsinhNorm, FuncNorm), __call__ transforms [vmin, vmax] through the scale and requires both results to be finite. With a log scale, a vmin or vmax <= 0 transforms to NaN/masked and raises 'Invalid vmin or vmax'. Data values outside the domain are only masked; it is the limits themselves that must be valid.

Source

Thrown at lib/matplotlib/colors.py:2957

            inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD),
            *bound_init_signature.parameters.values()])

        def __call__(self, value, clip=None):
            value, is_scalar = self.process_value(value)
            if self.vmin is None or self.vmax is None:
                self.autoscale_None(value)
            if self.vmin > self.vmax:
                raise ValueError("vmin must be less or equal to vmax")
            if self.vmin == self.vmax:
                return np.full_like(value, 0)
            if clip is None:
                clip = self.clip
            if clip:
                value = np.clip(value, self.vmin, self.vmax)
            t_value = self._trf.transform(value).reshape(np.shape(value))
            t_vmin, t_vmax = self._trf.transform([self.vmin, self.vmax])
            if not np.isfinite([t_vmin, t_vmax]).all():
                raise ValueError("Invalid vmin or vmax")
            t_value -= t_vmin
            t_value /= (t_vmax - t_vmin)
            t_value = np.ma.masked_invalid(t_value, copy=False)
            return t_value[0] if is_scalar else t_value

        def inverse(self, value):
            if not self.scaled():
                raise ValueError("Not invertible until scaled")
            if self.vmin > self.vmax:
                raise ValueError("vmin must be less or equal to vmax")
            t_vmin, t_vmax = self._trf.transform([self.vmin, self.vmax])
            if not np.isfinite([t_vmin, t_vmax]).all():
                raise ValueError("Invalid vmin or vmax")
            value, is_scalar = self.process_value(value)
            rescaled = value * (t_vmax - t_vmin)
            rescaled += t_vmin
            value = (self._trf
                     .inverted()

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Use strictly positive limits: vmin = max(vmin, smallest_positive_value)
  2. For zero-crossing data use SymLogNorm(linthresh=...) or AsinhNorm instead of LogNorm
  3. Let autoscale derive limits from the positive values rather than pinning vmin=0

Example fix

# before
norm = LogNorm(vmin=0, vmax=100)      # ValueError

# after
norm = LogNorm(vmin=1e-3, vmax=100)
# or for data crossing zero:
norm = SymLogNorm(linthresh=1e-3, vmin=-100, vmax=100)
Defensive patterns

Strategy: validation

Validate before calling

if norm.__class__.__name__ == 'LogNorm' and (vmin <= 0 or vmax <= 0):
    raise ValueError(f'LogNorm limits must be positive; got vmin={vmin}, vmax={vmax}')
normed = norm(data)

Type guard

def lognorm_range_ok(vmin, vmax) -> bool:
    return vmin > 0 and vmax > 0 and vmin <= vmax

Try / catch

try:
    normed = norm(data)
except ValueError as e:
    if 'Invalid vmin or vmax' in str(e) and isinstance(norm, LogNorm):
        norm = SymLogNorm(linthresh=max(vmin, 1e-3), vmin=vmin, vmax=vmax)
        normed = norm(data)
    else:
        raise

Prevention

When it happens

Trigger: LogNorm(vmin=0, vmax=100)(data); LogNorm(vmin=-5, vmax=5); pinning vmax=0 for all-negative data; FuncNorm whose inverse is undefined at the chosen limits.

Common situations: Log-scaling datasets bounded by zero (copying linear-norm limits into log plots); choropleth/count data where 0 was the old vmin; defaults that insert 0 as the lower bound.

Related errors


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