ValueError: math domain error
Exception: ValueErrorCategory: runtime (math module)Raised at: the math function call that received an out-of-domain argument
What this error means
Every function in the math module works on real numbers only, and each one is defined over a specific range of inputs. That range is its *domain*. Pass an input outside the domain and there is no real-number answer to return, so Python raises ValueError.
This is a ValueError, not a TypeError, and the difference matters for how you catch it. The type was correct — you passed a number. The *value* was not usable.
Traceback (most recent call last):
File "demo.py", line 3, in <module>
x = math.sqrt(-4)
ValueError: math domain error
Which functions, which domains
import math
math.sqrt(-4) # 💥 needs x >= 0
math.log(0) # 💥 needs x > 0
math.log(-5) # 💥 needs x > 0
math.log10(0) # 💥 needs x > 0
math.log2(-1) # 💥 needs x > 0
math.asin(2) # 💥 needs -1 <= x <= 1
math.acos(1.5) # 💥 needs -1 <= x <= 1
math.atan(999) # ✅ all reals are fine
math.exp(-1e9) # ✅ underflows to 0.0, no error
math.floor(3.7) # ✅ all reals are fine
Two families cause almost every real-world report. sqrt of a negative number, which usually means an earlier calculation went wrong — a variance came out negative, or a discriminant b² - 4ac did. And log of zero or a negative, which usually means a count was empty or a probability was zero.
Why `math.sqrt(-4)` fails but `(-4) ** 0.5` does not
This surprises people, and the reason is a deliberate design difference rather than an inconsistency:
import math
math.sqrt(-4) # 💥 ValueError: math domain error
(-4) ** 0.5 # ✅ (1.2246467991473532e-16+2j) — a complex number
(-8) ** (1/3) # ✅ complex as well
math is defined strictly over the reals and refuses to invent an answer. The operator is more permissive and falls back to complex arithmetic when there is no real result. So 0.5 never raises — which means it will not warn you that your input was invalid. If a negative value indicates a bug upstream, math.sqrt failing loudly is the behaviour you want.
When complex numbers are the right answer
Sometimes a complex result is genuinely what you want — solving quadratics with negative discriminants, signal processing, or electrical engineering. That is what cmath is for, and it mirrors the math API:
import cmath
cmath.sqrt(-4) # ✅ 2j
cmath.log(-1) # ✅ 3.141592653589793j
cmath.asin(2) # ✅ (1.5707963267948966+1.3169578969248166j)
# solving x^2 + 1 = 0
a, b, c = 1, 0, 1
disc = b*b - 4*a*c
for r in ((-b + cmath.sqrt(disc)) / (2*a),
(-b - cmath.sqrt(disc)) / (2*a)):
print(r) # 1j and -1j
Use cmath when complex output is meaningful. Do not use it merely to silence the error — if a negative square root means your data is corrupt, cmath will hand you a complex number and your program will continue with nonsense.
Guarding the input
# 1 — check the domain explicitly (best when the value should never be negative)
if variance < 0:
raise ValueError(f"variance cannot be negative, got {variance}")
sd = math.sqrt(variance)
# 2 — clamp when a small negative is expected rounding noise
sd = math.sqrt(max(variance, 0.0))
# 3 — skip the unusable rows
for p in probs:
if p > 0:
total += math.log(p)
# 4 — handle it locally
try:
x = math.log(value)
except ValueError:
x = float("nan")
Which guard you pick depends on whether the bad value is a bug or expected. If a negative variance is impossible, option 1 is correct — raise loudly and find out why it happened. If it is floating-point rounding on a value that should be exactly zero, option 2 is right. Catching ValueError and substituting nan (option 4) lets the calculation continue but makes the result quietly wrong further downstream, so use it only when you have a real plan for nan.
Related messages worth knowing
- ValueError: the parent guide — right type, wrong value — the whole family of conversion and range failures.
- ZeroDivisionError — the division-shaped cousin, with the same guard patterns.
- TypeError — what you get when the argument is the wrong type instead of the wrong value.
🇮🇳 Hindi में समझें
ValueError: math domain error tab aata hai jab aap math module ke kisi function ko aisa input dete ho jo uske domain ke bahar ho. Har math function real numbers par kaam karta hai aur har ek ki ek valid range hoti hai. math.sqrt(-4) fail hoga kyunki negative ka real square root nahi hota. math.log(0) ya math.log(-5) fail hoga kyunki log sirf positive numbers par defined hai. math.asin(2) fail hoga kyunki asin sirf -1 se 1 ke beech kaam karta hai. Ye ValueError hai, TypeError nahi — matlab type sahi tha (aapne number hi diya), lekin value usable nahi thi. Real world me do wajah sabse common hain: negative ka square root, jo aksar matlab hai pehle ka calculation galat tha (jaise variance negative aa gaya), aur zero ya negative ka log, jo aksar matlab hai koi count khaali tha. Ek confusing baat: math.sqrt(-4) error deta hai lekin (-4) 0.5 nahi deta — wo complex number (1.22e-16+2j) de deta hai. math strictly real numbers par kaam karta hai aur jawab invent nahi karta, jabki operator complex arithmetic par fall back kar jaata hai. Isliye agar negative value ek bug hai to math.sqrt ka loud failure behtar hai. Complex chahiye ho to cmath use karo — cmath.sqrt(-4) aapko 2j dega.