TheAlgorithms/Python · error · Exception

Years to repay must be an integer > 0

Error message

Years to repay must be an integer > 0

What it means

Raised by equated_monthly_installments() in financial/equated_monthly_installments.py when years_to_repay <= 0 OR years_to_repay is not an int. The tenure must be a whole number of years because it is multiplied by 12 to get the payment count; floats like 2.5 are rejected even though they are positive. It is the third and last check, so principal and rate are already valid when it fires.

Source

Thrown at financial/equated_monthly_installments.py:42

    >>> equated_monthly_installments(0, 0.12, 3)
    Traceback (most recent call last):
        ...
    Exception: Principal borrowed must be > 0
    >>> equated_monthly_installments(25000, -1, 3)
    Traceback (most recent call last):
        ...
    Exception: Rate of interest must be >= 0
    >>> equated_monthly_installments(25000, 0.12, 0)
    Traceback (most recent call last):
        ...
    Exception: Years to repay must be an integer > 0
    """
    if principal <= 0:
        raise Exception("Principal borrowed must be > 0")
    if rate_per_annum < 0:
        raise Exception("Rate of interest must be >= 0")
    if years_to_repay <= 0 or not isinstance(years_to_repay, int):
        raise Exception("Years to repay must be an integer > 0")

    # Yearly rate is divided by 12 to get monthly rate
    rate_per_month = rate_per_annum / 12

    # Years to repay is multiplied by 12 to get number of payments as payment is monthly
    number_of_payments = years_to_repay * 12

    return (
        principal
        * rate_per_month
        * (1 + rate_per_month) ** number_of_payments
        / ((1 + rate_per_month) ** number_of_payments - 1)
    )


if __name__ == "__main__":
    import doctest

View on GitHub (pinned to f5988cc097)

Solutions

  1. Pass an int: years_to_repay=3, not 3.0 or '3'.
  2. Coerce deliberately: `years = int(round(months / 12))` — but verify rounding half-year tenures is acceptable for your use case.
  3. For fractional years, call with int years and handle the remainder separately, or use a formula that accepts months directly.

Example fix

# before
emi = equated_monthly_installments(25000, 0.12, 36 / 12)  # 3.0 is not int -> Exception

# after
emi = equated_monthly_installments(25000, 0.12, int(36 / 12))  # 3
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(years_to_repay, int) or years_to_repay <= 0:
    # bool is an int subclass; exclude it explicitly if needed
    raise LoanInputError("years_to_repay must be an int > 0")

Type guard

def valid_years(y) -> bool:
    return isinstance(y, int) and not isinstance(y, bool) and y > 0

Try / catch

try:
    emi = equated_monthly_installments(principal, rate, int(years_to_repay))
except (TypeError, Exception) as exc:
    raise LoanInputError(str(exc)) from exc

Prevention

When it happens

Trigger: equated_monthly_installments(25000, 0.12, 0); passing 2.5 years, '3' (string), or 3.0 (float) — 3.0 fails the isinstance(int) test; tenures computed as float divisions like 36/12 producing 3.0.

Common situations: JSON/YAML configs where the tenure is parsed as 3.0; converting months to years with division and forgetting to int(); user input arriving as a string from a CLI.

Related errors


AI-assisted analysis of TheAlgorithms/Python@f5988cc097 (2026-08-14). Data as JSON: /api/errors/3ced6cd62c540f3f. Report an issue: GitHub.