headroomlabs-ai/headroom · error · ValueError

expected a finite number, got {value!r}

Error message

expected a finite number, got {value!r}

What it means

ValueError from _coerce in headroom/settings_store.py when a 'float' field receives a value that parses to a non-finite float — NaN, +inf, or -inf (math.isfinite check at line 785). Note the value arrives as a Python float here, so json.loads('Infinity') (which Python's json accepts by default) or float('nan') from a caller's parsing will trigger it. Surfaces via SettingsValidationError.field_errors.

Source

Thrown at headroom/settings_store.py:785

        if field.type == "optional-bool" and token == "":
            return None
        if token in ("1", "true", "yes", "on"):
            return True
        if token in ("0", "false", "no", "off", ""):
            return False
        raise ValueError(f"expected a boolean, got {value!r}")
    if field.type in ("int", "float"):
        if isinstance(value, bool):  # bool is an int subclass — reject explicitly
            raise ValueError(f"expected a number, got {value!r}")
        number: int | float
        if field.type == "int":
            if isinstance(value, float) and not value.is_integer():
                raise ValueError(f"expected an integer, got {value!r}")
            number = int(value)
        else:
            number = float(value)
            if not math.isfinite(number):
                raise ValueError(f"expected a finite number, got {value!r}")
        if field.minimum is not None and number < field.minimum:
            raise ValueError(f"must be >= {field.minimum}")
        if field.maximum is not None and number > field.maximum:
            raise ValueError(f"must be <= {field.maximum}")
        return number
    if field.type == "enum":
        token = str(value)
        if token not in field.choices:
            raise ValueError(f"{token!r} not one of {list(field.choices)}")
        return token
    if field.type == "csv-list":
        tokens = value if isinstance(value, list | tuple) else str(value).split(",")
        tokens = [str(token).strip() for token in tokens]
        tokens = [token for token in tokens if token]
        return ",".join(tokens) if tokens else None
    if field.type == "header-map":
        if isinstance(value, dict):
            parsed = value

View on GitHub (pinned to 322425c43b)

Solutions

  1. Fix the upstream computation so it never yields inf/NaN (guard divisions, use a large finite default).
  2. If the setting means 'no limit', use the field's documented maximum or omit it (null), not infinity.
  3. Validate with math.isfinite() before saving.

Example fix

# before
save({'timeout': float(x) / count})  # inf when count == 0

# after
save({'timeout': float(x) / count if count else 3600.0})
Defensive patterns

Strategy: validation

Validate before calling

import math

def finite_float(v) -> bool:
    try:
        return math.isfinite(float(v))
    except (TypeError, ValueError):
        return False

Type guard

import math

def is_finite_number(v) -> bool:
    return not isinstance(v, bool) and isinstance(v, (int, float)) and math.isfinite(v)

Try / catch

except SettingsValidationError as e:
    for key, msg in e.field_errors.items():
        if 'finite' in msg:
            payload.pop(key, None)  # drop and fall back to default
    store.save(payload)

Prevention

When it happens

Trigger: save({'request_timeout': float('inf')}); a JSON payload containing Infinity/NaN literals (Python's json module accepts them); computed ratios like x/0.0 producing inf and then passed to the store.

Common situations: Division-by-zero bugs upstream that leak inf into config; numpy calculations returning np.inf/np.nan converted with float(); JSON produced by tools that emit non-standard Infinity literals.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/d4d4f8a152dc4f68. Report an issue: GitHub.