ValueError in Python: where it comes from and how to fix it

ValueError appears when a function gets the right type of data but a value it cannot use. Where it comes from, how to fix it.
5 min read
Believemy logo

Definition

A Python function often expects more than a type: it expects a value that makes sense. Hand it a string where it wants one, and it will not complain, the type is right. But if the content does not match anything the function knows how to handle, it needs another way to say so: that is the role of this exception, ValueError.

PYTHON
age = int("twelve")  # a number was expected, a word was typed instead

# ValueError: invalid literal for int() with base 10: 'twelve'

The string "twelve" is indeed a string, and int accepts strings just fine as an argument. That is not where the problem lies: this particular piece of text simply holds no number that can be recognised. This distinction changes where to look when something breaks. Passing a list to the same function would have produced a TypeError instead, since the complaint would then be about the nature of the argument rather than its content.


The difference with TypeError

Mixing the two errors up sends you looking in the wrong place for long minutes, because you end up fixing the type of a value that was already the right type. Three calls are enough to see exactly where the line falls.

CallResultReason
int("12")No errorA string that does hold a number
int("twelve")ValueErrorThe right type, content that cannot convert
int([1, 2])TypeErrorA type the function cannot handle at all

The distinction becomes almost automatic once these three cases are clear: if fixing the content of the value is enough to make the call succeed, it is a ValueError; if its nature has to change, it is a TypeError. The traceback always shows the offending value on its last line, no need to guess it.


Where it shows up most often

Converting user input comes first by a wide margin, for a simple reason: an input always hands back text, whatever the person actually typed. Converting that text with int or float makes the whole program depend on whatever happens to be on the keyboard at that moment.

Unpacking comes next, for a related reason. Multiple assignment demands an exact number of items on both sides of the equals sign, and a split on a malformed line does not always supply that many.

PYTHON
surname, first = "Smith".split(";")  # the expected line contained a semicolon

# ValueError: not enough values to unpack (expected 2, got 1)

Then come failed lookups, such as items.index(value) or items.remove(value) called on a missing element, followed by maths functions asked to work outside their domain, such as a square root on a negative number. These situations share the same root cause: the data comes from outside the program, and nobody checked it before using it.


Catching it, or raising it yourself

Catching is done with try and except, targeting the precise error rather than too broad a class.

PYTHON
while True:
    entry = input("Your age: ")
    try:
        age = int(entry)
        break
    except ValueError:
        print("Digits only, please.")
Warning

Writing except Exception instead of except ValueError looks safer, but it also swallows typos in a variable name or a missing key lookup. The program keeps running in silence, and the real bug only surfaces much later, somewhere else.

The other half of the job is raising ValueError inside your own functions with raise. A function receiving a negative duration or a month of 13 has every reason to refuse right away, with a message naming the value it got, rather than letting absurd data travel across ten more calls.

PYTHON
def apply_discount(price, percent):
    if not 0 <= percent <= 100:
        raise ValueError(f"percent out of bounds: {percent}")
    return price * (1 - percent / 100)


The real fix sits upstream

A ValueError caught in silence has fixed nothing at all, it has only hidden the symptom. It almost always signals that a piece of data entered the program without being checked, and the place where it blows up is rarely the place where the problem was born.

The method that saves the most time is validating at the border, at the moment the text is typed, the file is read, or the answer from an outside service is decoded. Past that point the rest of the program works on data whose shape is guaranteed, and the error disappears from the deeper layers of the code, exactly where it costs the most to understand.


Frequently asked questions

Question

How can input be converted without risking the error?

There is no forgiving conversion in Python: int either succeeds or raises. The only reliable approach is wrapping the call in a try, or validating the string beforehand with a method such as isdigit, which handles neither a negative sign nor decimals.

Question

Why does my unpacking fail on a single line of the file?

Because that particular line does not match the expected shape: a missing separator, an empty field at the end, or a trailing blank line. Setting a maximum number of splits with split(";", 1), or checking the length of the result before assigning, settles the problem for good.

Question

Should this error be raised, or should a custom exception be created?

As long as the problem boils down to a rejected value, the standard error stays the better choice: everyone already knows it and knows how to catch it. A custom exception is only justified when the caller has to tell this exact case apart from the others in order to react differently, which is rarer than it looks.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.