ZhuLinsen/daily_stock_analysis · error · ValueError

{field_name} must be an integer

Error message

{field_name} must be an integer

What it means

Raised by _int_in_range (src/services/alert_indicators.py:382): the value converted to int successfully, but its canonical string form does not equal str(number) or f'{number}.0', i.e. it is a non-integer-shaped value. This deliberately rejects inputs like "12.5", 3.7, or "1e2" that int() would otherwise truncate, preventing silently accepted-but-wrong periods.

Source

Thrown at src/services/alert_indicators.py:382

        )
    return params


def _direction(value: Any, allowed: frozenset[str], *, default: str) -> str:
    direction = str(value or default).strip().lower()
    if direction not in allowed:
        raise ValueError(f"invalid direction: {direction}")
    return direction


def _int_in_range(value: Any, field_name: str, *, default: int, minimum: int = 2, maximum: int = 250) -> int:
    raw_value = default if value is None or value == "" else value
    try:
        number = int(raw_value)
    except (TypeError, ValueError) as exc:
        raise ValueError(f"invalid {field_name}: {value}") from exc
    if str(raw_value).strip() not in {str(number), f"{number}.0"}:
        raise ValueError(f"{field_name} must be an integer")
    if number < minimum or number > maximum:
        raise ValueError(f"{field_name} must be between {minimum} and {maximum}")
    return number


def _finite_float(value: Any, field_name: str) -> float:
    try:
        number = float(value)
    except (TypeError, ValueError) as exc:
        raise ValueError(f"invalid {field_name}: {value}") from exc
    if not isfinite(number):
        raise ValueError(f"{field_name} must be finite")
    return number


def _float_in_range(
    value: Any,
    field_name: str,

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Round or floor the value deliberately on the client before submitting: Math.round(value).
  2. Use a stepper input with step=1 for period fields.
  3. If a decimal period ever seems meaningful, the indicator does not support it — pick an integer design instead.

Example fix

// before
{ "alert_type": "ma_price_cross", "parameters": { "window": 20.5 } }

// after
{ "alert_type": "ma_price_cross", "parameters": { "window": Math.round(20.5) /* 21 */ } }
Defensive patterns

Strategy: validation

Validate before calling

def to_int_field(v):
    if v is None or v == '':
        return None  # default
    f = float(v)
    if f != int(f):
        raise ValueError('fractional period not allowed')
    return int(f)
params = {k: (to_int_field(v) if k in INT_FIELDS else v) for k, v in params.items()}

Type guard

def is_integer_shaped(v) -> bool:
    if v is None or v == '':
        return True
    try:
        s = str(v).strip(); n = int(v)
    except (TypeError, ValueError):
        return False
    return s in {str(n), f'{n}.0'}

Try / catch

try:
    normalize_indicator_parameters(alert_type, params)
except ValueError as e:
    if 'must be an integer' in str(e):
        for k in INT_FIELDS & params.keys():
            params[k] = int(float(params[k])) if str(params[k]).replace('.','',1).isdigit() else params[k]
        normalize_indicator_parameters(alert_type, params)
    else:
        raise

Prevention

When it happens

Trigger: parameters like {"window": 20.5}, {"period": "9.25"}, {"fast_period": 12.5}. Accepted shapes are exact ints (20), int strings ("20"), and float-with-.0 (20.0, "20.0") — because int(20.9) is 20, the guard catches the fractional part.

Common situations: Sliders or step-less inputs emitting fractional values; unit conversion producing decimals (e.g. 26 * 0.5); spreadsheets storing numbers as decimals ("12,0" in EU locale parses oddly).

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/29992bfa09cc1684. Report an issue: GitHub.