tui-cs/Terminal.Gui · error · ArgumentOutOfRangeException

Zoom level must be a finite number.

Error message

Zoom level must be a finite number.

What it means

Thrown by ImageView.SetZoomLevel when the supplied zoomLevel is NaN or infinite. Zoom is a multiplier applied to source pixel dimensions and used in clamp/compare math, so a non-finite value would poison layout and rendering arithmetic. The value is clamped to [GetMinimumZoomLevel(), MAX_ZOOM_LEVEL] after the check, so only truly broken numbers (division-by-zero results, uninitialised doubles) reach the guard.

Source

Thrown at Terminal.Gui/Views/ImageView/ImageView.Input.cs:68

        _centerX = centerX;
        _centerY = centerY;
        ClampCenter ();

        if (Math.Abs (previousCenterX - _centerX) < double.Epsilon && Math.Abs (previousCenterY - _centerY) < double.Epsilon)
        {
            return false;
        }

        InvalidateScaledImage ();

        return true;
    }

    private bool SetZoomLevel (double zoomLevel, Point? anchor)
    {
        if (double.IsNaN (zoomLevel) || double.IsInfinity (zoomLevel))
        {
            throw new ArgumentOutOfRangeException (nameof (zoomLevel), @"Zoom level must be a finite number.");
        }

        double previousZoomLevel = _zoomLevel;
        double clampedZoomLevel = Math.Clamp (zoomLevel, GetMinimumZoomLevel (), MAX_ZOOM_LEVEL);

        if (Math.Abs (previousZoomLevel - clampedZoomLevel) < double.Epsilon)
        {
            return false;
        }

        if (anchor is { } position && TryMapViewportPointToSourceCenter (position, out double sourceX, out double sourceY))
        {
            _zoomLevel = clampedZoomLevel;
            SetCenterForAnchor (position, sourceX, sourceY);
        }
        else
        {
            _zoomLevel = clampedZoomLevel;

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Guard the divisor before computing zoom: if (image.Width == 0) return; double zoom = viewport / (double)image.Width;
  2. Sanitise external input with double.IsFinite(zoomLevel) before calling SetZoomLevel or setting ZoomLevel.
  3. Default uninitialized zoom fields to a finite sentinel (e.g. 1.0) rather than leaving them as NaN.
  4. Use the public clamped API (ZoomLevel property) which routes through the same guard, so fix the source of the bad value upstream.

Example fix

// before
double zoom = viewportWidth / (double)image.Width; // NaN if Width==0
imageView.ZoomLevel = zoom;

// after
double zoom = image.Width > 0 ? viewportWidth / (double)image.Width : 1.0;
imageView.ZoomLevel = zoom;
Defensive patterns

Strategy: validation

Validate before calling

double zoom = ComputeZoom();
if (!double.IsFinite(zoom))
{
    zoom = 1.0; // safe default
}
imageView.ZoomLevel = zoom;

Type guard

static bool IsValidZoom(double z) => double.IsFinite(z) && z > 0;

Prevention

When it happens

Trigger: Computing zoomLevel from a ratio where the denominator is zero or infinity (e.g. image.Width == 0 -> width/0.0); reading zoom from parsed/config data that defaulted to NaN; chaining double transformations that accumulate NaN via 0*Inf.

Common situations: Auto-fit logic that divides by an image dimension before the image has loaded; JSON/deserialised config where the field was never set; math derived from an empty viewport size.

Related errors


AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13). Data as JSON: /api/errors/65ee9f5d710d0849. Report an issue: GitHub.