Python ValueError: invalid literal for int() with base 10
ValueError: invalid literal - cannot convert value
Verified against CPython 3.13 source (Objects/longobject.c), Python docs: built-in-exceptions, Python docs: built-in-functions (int) · Updated April 2026
> quick_fix
A function got an argument of the right type but with a value it can't process. Most common: int('abc') - str is the right type for int() but 'abc' isn't parseable. Validate input before conversion, or wrap in try-except ValueError to handle malformed values.
# Raises ValueError
age = int('twenty')
# Fix 1 - validate first
text = 'twenty'
if text.isdigit():
age = int(text)
else:
age = 0
# Fix 2 - try-except
try:
age = int(text)
except ValueError:
age = 0
# Fix 3 - regex parse for complex inputs
import re
m = re.match(r'^\d+$', text)
age = int(m.group()) if m else 0What causes this error
ValueError indicates the type is correct but the value is invalid for the operation. Different from TypeError (type itself wrong). Common contexts: int(), float(), datetime.strptime(), JSON parsing of malformed data, struct.unpack on wrong-sized buffer, or any function that validates value semantics after accepting the type.
How to fix it
- 01
step 1
Read the full error message
ValueError: invalid literal for int() with base 10: 'twenty' - shows the function (int), the base, and the actual offending value.
- 02
step 2
Identify the source of the bad value
It's almost always external input: a CSV column, a form field, a JSON value, an environment variable. Trace the value back to where it entered your code.
- 03
step 3
Validate before converting
Use str.isdigit() for non-negative integers, regex for more flexibility, or try-except ValueError for general parsing.
- 04
step 4
For datetime.strptime, match the format
datetime.strptime('2026/01/01', '%Y-%m-%d') raises - the separator is /, not -. Use a flexible parser like dateutil or normalise input first.
from datetime import datetime try: dt = datetime.strptime(value, '%Y-%m-%d') except ValueError: dt = None - 05
step 5
For numeric overflow, use Decimal or check ranges
int() handles arbitrary precision but float() does not - float('1e500') raises OverflowError, but float('not_a_number') raises ValueError. Check input ranges if the source can have huge values.
How to verify the fix
- ValueError no longer fires on the offending value.
- Malformed input case handled with a clear default or error.
- Edge cases (empty string, whitespace, leading zeros) handled.
Why ValueError happens at the runtime level
ValueError originates from PyExc_ValueError defined in Objects/exceptions.c. It's raised by C-level functions when they receive a parameter of the right type but with content that can't be processed. For int(s), the parser in Objects/longobject.c (long_from_string) walks the string, validating each character against the digit set for the chosen base; on the first invalid character, it sets a ValueError with a 'invalid literal for int()' message. For datetime.strptime, the format-string parser raises when the input doesn't match the format. The contract is: types are checked first via tp_check_args, values are checked second via parser-specific logic.
Common debug mistakes for ValueError
- Calling int() on a string with leading/trailing whitespace expecting it to fail - actually int() strips whitespace by default, so int(' 42 ') works; but int('42abc') raises.
- Using str.isnumeric() instead of isdigit() for int parsing - isnumeric matches Unicode numeric characters that int() can't parse (Roman numerals, fractions), giving false positives.
- Catching ValueError without distinguishing from KeyError or IndexError - bundling all input-validation errors loses information needed to give a useful error message to the user.
- Using float(s) for monetary values without checking - floats can't represent 0.1 exactly, and ValueError catches malformed input but not precision loss; use Decimal for money.
- Calling json.loads on a config file without try-except - any malformed JSON raises ValueError (json.JSONDecodeError is a subclass), and the default error message doesn't show the offending byte position clearly.
When ValueError signals a deeper problem
Persistent ValueError at runtime usually means input validation is happening at the wrong layer. When validation lives inside business logic (deep in a function that does both parsing and computation), every malformed input bubbles up as ValueError with a stack trace that hides the entry point. The architectural fix is a typed boundary library at the input edge - Pydantic v2 for HTTP requests, attrs converters for internal data, dataclass __post_init__ for simple models. These convert raw strings to typed values once, with clear errors at the source. Internal code then operates on validated types, so ValueError becomes impossible by construction.
Frequently asked questions
Why isn't 'twenty' parseable but '20' is?
int() with base 10 only parses ASCII digits 0-9 and an optional leading + or -. Word forms ('twenty') need a different parser like word2number library.
What's the difference between ValueError and TypeError?
TypeError - the type is wrong (passing a list to int()). ValueError - the type is right but the value can't be processed (passing 'abc' to int()).
Should I catch ValueError or check first?
Pythonic style is EAFP (easier to ask forgiveness than permission) - try the conversion, catch the error. LBYL (look before you leap) is fine when the check is cheap. For int parsing, EAFP is faster.