an unconstrained denominator

Divide by Zero

A division is a promise that the denominator is non-zero, and the type system rarely keeps that promise. Integer division throws, floats quietly yield Infinity or NaN, and either way an unvalidated input poisons everything downstream.

Nothing stops the divisor from being zero until the program meets it.

01in the wild

In the wild

Ohm's Law, Unguarded

Ohm's law rearranged: I = V / R. Nothing stops R from being zero until the program meets it.

example.py
# V = I * R  ->  I = V / R
def current(voltage, resistance):
    return voltage / resistance        # boom when resistance == 0

# RIGHT: make the impossible value unrepresentable
def current(voltage, resistance):
    if resistance == 0:
        raise ValueError("resistance must be non-zero")
    return voltage / resistance
The denominator is unconstrained input. Validate it at the boundary instead of trusting the caller.
// observed
current(5, 0) -> ZeroDivisionError
guarded     -> ValueError('resistance must be non-zero')
example.js
// JS won't throw -- it returns Infinity or NaN, which spreads
5 / 0;        // Infinity
0 / 0;        // NaN
[Infinity].map(x => x * 2);   // [Infinity], silently wrong

// RIGHT: validate, return a Result-like value
const current = (v, r) =>
  r === 0 ? { ok: false, error: "r is zero" } : { ok: true, value: v / r };
Worse than a crash: JS yields Infinity/NaN that quietly poison every later calculation.
// observed
5/0 -> Infinity (no error)
current(5,0) -> { ok: false, error: 'r is zero' }
example.rs
// Integer divide by zero PANICS; float gives inf/NaN.
// Rust pushes you to encode the failure in the type.
fn current(v: f64, r: f64) -> Result<f64, &'static str> {
    if r == 0.0 { return Err("resistance is zero"); }
    Ok(v / r)
}

match current(5.0, 0.0) {
    Ok(i)  => println!("{i} A"),
    Err(e) => eprintln!("rejected: {e}"),
}
Result<T, E> makes the caller acknowledge the failure path. The unhappy case cannot be forgotten.
// observed
current(5.0, 0.0) -> Err("resistance is zero")
handled by match, never ignored

Averages Over an Empty Collection

Every average divides by a count, and the count is zero exactly when you forgot to check.

example.py
def average(scores):
    return sum(scores) / len(scores)   # ZeroDivisionError on []

# RIGHT: define the empty case before you divide
def average(scores):
    if not scores:
        return None                    # or 0.0, but say so on purpose
    return sum(scores) / len(scores)
An empty list is a perfectly valid input; the division is what's invalid. Decide what 'the average of nothing' means at the boundary.
// observed
average([]) -> ZeroDivisionError
guarded   -> None
example.js
const mean = xs => xs.reduce((a, b) => a + b, 0) / xs.length;
mean([]);            // NaN -- and NaN spreads silently

// RIGHT: handle the empty case, return a Result-like value
const mean = xs =>
  xs.length === 0
    ? { ok: false }
    : { ok: true, value: xs.reduce((a, b) => a + b, 0) / xs.length };
JS won't throw; it returns NaN, which compares false against everything and quietly corrupts charts, sums, and thresholds.
// observed
mean([]) -> NaN (no error)
guarded  -> { ok: false }
02cross-pollination

Where this compounds

03weakness catalog

Mapped weaknesses (CWE)

On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.