Skip to content

Fix: Python ValueError: invalid literal for int() with base 10

FixDevs ·

Quick Answer

How to fix Python ValueError invalid literal for int() with base 10 caused by passing float strings, whitespace, empty strings, or non-numeric characters to int().

The Error

You try to convert a string to an integer and Python throws this traceback:

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

The exact value after the colon varies. You might see:

ValueError: invalid literal for int() with base 10: '3.14'
ValueError: invalid literal for int() with base 10: ''
ValueError: invalid literal for int() with base 10: '42\n'
ValueError: invalid literal for int() with base 10: ' 7 '

Every one of these has the same root cause: int() received a string it cannot parse as a base-10 integer.

Why This Happens

Python’s int() function is strict. It only accepts strings that represent a whole number with no extra characters. That means:

  • No decimal points. "3.14" is a valid float, but int("3.14") fails because int() does not truncate floats for you when the input is a string.
  • No whitespace inside the number. While int(" 42 ") actually works (leading/trailing spaces are tolerated), int("4 2") does not.
  • No trailing newlines from file reads. Reading lines from a file or input() often leaves a \n at the end. int("42\n") actually works in modern Python, but int("42\r\n") can fail on some platforms, and int("\n") always fails.
  • No empty strings. int("") raises the error immediately.
  • No non-numeric characters. Letters, symbols, commas ("1,000"), currency signs ("$50") — none of these are valid.

The function expects a string like "42", "-7", or "+100". Anything else triggers the ValueError.

This error shows up most often when you process user input, CSV data, API responses, or file contents without validating or cleaning the string first.

If you are running into similar type-related issues with your Python setup, check out the guide on fixing Python command not found errors to make sure your environment is configured correctly.

Fix 1: Strip Whitespace and Newlines Before Converting

The most common cause is invisible whitespace. Data from files, HTTP responses, or input() often carries trailing newlines or spaces.

Use str.strip() before passing the value to int():

raw = "  42 \n"
number = int(raw.strip())
print(number)  # 42

strip() removes all leading and trailing whitespace, including spaces, tabs (\t), newlines (\n), and carriage returns (\r).

If you only want to remove specific characters:

raw = "42\n"
number = int(raw.rstrip("\n"))

When reading lines from a file, always strip:

with open("numbers.txt") as f:
    for line in f:
        value = int(line.strip())
        print(value)

Pro Tip: str.strip() is safe to call even if there is no whitespace. Make it a habit to strip every string before converting it to an integer — it costs nothing and prevents hard-to-debug errors from invisible characters like \r on Windows.

Fix 2: Handle Float Strings — Convert Through float() First

If your string contains a decimal point like "3.14" or "100.0", int() rejects it outright. You need to go through float() first:

value = "3.14"
number = int(float(value))
print(number)  # 3

This first parses the string as a float, then truncates the decimal part. Be aware that int() truncates toward zero, not rounds:

int(float("3.9"))   # 3, not 4
int(float("-3.9"))  # -3, not -4

If you need proper rounding:

number = round(float("3.9"))  # 4

This pattern is especially common when reading CSV files where numeric columns may contain values like "42.0" even though they represent integers:

import csv

with open("data.csv") as f:
    reader = csv.reader(f)
    for row in reader:
        count = int(float(row[2]))  # Column might contain "42.0"

If you need exact decimal precision (for financial data, for example), use Python’s Decimal class instead of float():

from decimal import Decimal

value = "19.99"
precise = Decimal(value)
truncated = int(precise)  # 19

Decimal avoids floating-point rounding issues that float() can introduce. For most general-purpose code, int(float(value)) is fine.

Fix 3: Handle Empty Strings

Empty strings are another frequent trigger. This happens when a form field is left blank, a CSV cell is empty, or a split operation produces empty entries:

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

Check before converting:

value = ""

if value:
    number = int(value)
else:
    number = 0  # or None, or skip the row

When splitting strings, empty entries can sneak in:

data = "1,,3,4"
parts = data.split(",")
# parts = ['1', '', '3', '4']

numbers = [int(p) for p in parts if p]  # Skip empty strings

A common pattern for CSV or tab-separated data:

numbers = [int(p.strip()) for p in parts if p.strip()]

This handles both empty strings and whitespace-only strings in one pass.

Fix 4: Remove Non-Numeric Characters

Strings like "$50", "1,000", "42kg", or "#7" all fail because they contain characters that int() does not understand.

Strip them out before converting:

# Remove dollar signs and commas
price = "$1,299"
number = int(price.replace("$", "").replace(",", ""))
print(number)  # 1299

For more complex cleanup, use a regex to extract only digits:

import re

raw = "Order #4521 confirmed"
match = re.search(r"-?\d+", raw)
if match:
    number = int(match.group())
    print(number)  # 4521

The pattern r"-?\d+" matches an optional minus sign followed by one or more digits. This extracts the first integer from any string.

To extract all integers:

raw = "3 apples and 12 oranges"
numbers = [int(x) for x in re.findall(r"-?\d+", raw)]
print(numbers)  # [3, 12]

Common Mistake: Do not blindly strip all non-digit characters and then convert. A string like "v2.1" would become "21" after stripping, which is not the number you wanted. Always understand the format of your data before cleaning it.

Fix 5: Validate User Input with try/except

When accepting numbers from input(), users can type anything. Never trust raw input:

user_input = input("Enter your age: ")
age = int(user_input)  # Crashes if user types "twenty" or ""

Wrap the conversion in a try/except block:

user_input = input("Enter your age: ")

try:
    age = int(user_input.strip())
except ValueError:
    print("Please enter a valid whole number.")
    age = None

For a retry loop that keeps asking until the user gives valid input:

while True:
    user_input = input("Enter your age: ").strip()
    try:
        age = int(user_input)
        if age < 0 or age > 150:
            print("Please enter a realistic age.")
            continue
        break
    except ValueError:
        print(f"'{user_input}' is not a valid number. Try again.")

This pattern is essential for any CLI tool or script that accepts user input. Without it, a single typo crashes the entire program.

If you are building Python scripts and running into import issues alongside input handling, the guide on fixing Python ModuleNotFoundError: No module named covers the most common causes.

Fix 6: Use str.isdigit() for Pre-Validation

If you want to check whether a string can be converted before attempting the conversion, str.isdigit() is useful — but limited:

value = "42"
if value.isdigit():
    number = int(value)

Limitations of isdigit():

Inputisdigit()int() works?
"42"TrueYes
"-7"FalseYes
"+3"FalseYes
"3.14"FalseNo
""FalseNo
" 42 "FalseYes

isdigit() returns False for negative numbers, numbers with leading +, and strings with whitespace — all of which int() handles fine.

A more reliable check:

def is_valid_int(s):
    """Check if string can be converted to int."""
    try:
        int(s)
        return True
    except (ValueError, TypeError):
        return False

This uses the “easier to ask forgiveness than permission” (EAFP) approach, which is the standard Python idiom. It catches TypeError too, handling cases where s is None or another non-string type.

An alternative using lstrip for sign-aware validation:

def is_int_string(s):
    """Check if string represents an integer, including negative numbers."""
    s = s.strip()
    if s.startswith(("+", "-")):
        return s[1:].isdigit()
    return s.isdigit()

Fix 7: Parse CSV and File Data Safely

File data is a major source of this error. CSV files might have headers, empty rows, mixed types, or encoding artifacts.

Skip headers and empty lines:

with open("data.csv") as f:
    next(f)  # Skip header row
    for line_num, line in enumerate(f, start=2):
        line = line.strip()
        if not line:
            continue
        parts = line.split(",")
        try:
            count = int(parts[1].strip())
        except (ValueError, IndexError) as e:
            print(f"Line {line_num}: skipping — {e}")
            continue

Using the csv module (recommended):

import csv

with open("data.csv", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        raw_value = row["quantity"].strip()
        if not raw_value:
            continue
        try:
            quantity = int(raw_value)
        except ValueError:
            # Maybe it's a float like "5.0"
            try:
                quantity = int(float(raw_value))
            except ValueError:
                print(f"Cannot parse quantity: {raw_value!r}")
                continue

Watch out for BOM characters. UTF-8 files saved by Excel sometimes start with a byte order mark (\ufeff). This invisible character attaches to the first value and breaks conversion:

# The string looks like "42" but is actually "\ufeff42"
value = "\ufeff42"
int(value)  # ValueError

# Fix: use utf-8-sig encoding
with open("data.csv", encoding="utf-8-sig", newline="") as f:
    reader = csv.reader(f)

The utf-8-sig encoding automatically strips the BOM. If you encounter mysterious ValueError errors on the very first value in a file, this is almost always the cause.

This kind of careful data handling pairs well with understanding Python’s error hierarchy. If you are hitting RecursionError in related data-processing code, the guide on fixing Python RecursionError explains maximum recursion depth issues.

Fix 8: Convert Between Number Bases

int() accepts a second argument for the base. If you pass the wrong base or forget to specify one, you get the error:

int("0xff")     # ValueError — int() sees "0xff" as base 10
int("0xff", 16) # 255 — correct

Common base conversions:

# Hexadecimal (base 16)
int("ff", 16)      # 255
int("0xff", 16)    # 255 — 0x prefix is allowed with base 16

# Binary (base 2)
int("1010", 2)     # 10
int("0b1010", 2)   # 10

# Octal (base 8)
int("77", 8)       # 63
int("0o77", 8)     # 63

# Auto-detect base from prefix
int("0xff", 0)     # 255 — base 0 means "figure it out from the prefix"
int("0b1010", 0)   # 10
int("0o77", 0)     # 63
int("42", 0)       # 42

Using base=0 tells int() to infer the base from standard prefixes (0x, 0b, 0o). This is useful when you do not know the format ahead of time.

Common mistake with base conversion: digits must be valid for the specified base. int("9", 8) fails because 9 is not a valid octal digit (octal uses 0–7 only).

int("9", 8)   # ValueError: invalid literal for int() with base 8: '9'
int("2", 2)   # ValueError: invalid literal for int() with base 2: '2'
int("g", 16)  # ValueError: invalid literal for int() with base 16: 'g'

Fix 9: Handle None and Non-String Types

Sometimes the error is not a ValueError but a TypeError — which happens when you pass a non-string, non-numeric type to int(). But if None or another object gets converted to a string first (e.g., through f-strings or concatenation), you can get the ValueError instead:

value = None
int(str(value))  # ValueError: invalid literal for int() with base 10: 'None'

Guard against this:

value = None

if value is not None:
    number = int(value)
else:
    number = 0

For a robust conversion function that handles multiple edge cases:

def safe_int(value, default=0):
    """Convert value to int safely, returning default on failure."""
    if value is None:
        return default
    try:
        return int(value)
    except (ValueError, TypeError):
        try:
            return int(float(value))
        except (ValueError, TypeError):
            return default

Use it anywhere you parse external data:

age = safe_int(request.get("age"))
count = safe_int(row.get("quantity"), default=-1)

This function tries int() first, falls back to int(float()) for decimal strings, and returns a default for anything else. It covers the vast majority of real-world parsing scenarios.

Fix 10: Debug the Actual Value

When the error message shows a value you did not expect, print the raw value with repr() to see hidden characters:

value = get_value_from_somewhere()
print(repr(value))  # Shows exact contents including \n, \r, \t, etc.

Example output:

' 42\r\n'

Now you know there is a carriage return and newline. strip() fixes it.

Another debugging technique — check the type:

value = get_value_from_somewhere()
print(type(value), repr(value))

This catches cases where the value is not a string at all (maybe it is a bytes object, a list, or None).

For JSON data specifically, watch out for values that look like numbers but are stored as strings. If you are parsing JSON and hitting type errors, the guide on fixing JSON parse unexpected token errors covers related parsing pitfalls.

When dealing with deeply nested code that processes these values, Python indentation errors can compound the confusion. Make sure your try/except blocks are indented correctly.

int() vs float() vs Decimal — When to Use Each

Choosing the right conversion function prevents errors before they happen:

FunctionInput "3.14"Input "42"Input "1e5"Precision
int()ValueError42ValueErrorExact
float()3.1442.0100000.0~15 digits
Decimal()Decimal('3.14')Decimal('42')Decimal('1E+5')Arbitrary

Use int() when you need whole numbers and want strict validation.

Use float() for general-purpose decimal numbers. Be aware of floating-point precision issues:

float("0.1") + float("0.2")  # 0.30000000000000004

Use Decimal() for financial calculations or when precision matters:

from decimal import Decimal
Decimal("0.1") + Decimal("0.2")  # Decimal('0.3')

If your input might be either an integer or a float string, the safest general approach is:

def parse_number(s):
    """Parse a string as int if possible, otherwise float."""
    s = s.strip()
    try:
        return int(s)
    except ValueError:
        return float(s)

This returns an int for "42" and a float for "3.14", giving you the most appropriate type automatically.

Still Not Working?

If you have tried the fixes above and still see the error:

  • Check encoding. Open the file with the correct encoding. Try encoding="utf-8-sig" for files from Excel or Windows tools.
  • Check for invisible Unicode characters. Some text editors or copy-paste operations insert zero-width spaces (\u200b), non-breaking spaces (\xa0), or other invisible characters. Use repr() to reveal them, then strip them:
value = value.replace("\u200b", "").replace("\xa0", " ").strip()
  • Check your data source. If the value comes from an API, database, or file, print the raw response to confirm it matches what you expect.
  • Check locale-specific number formats. Some locales use commas as decimal separators ("3,14" instead of "3.14") and periods as thousands separators ("1.000" meaning one thousand). Use locale or manually replace before converting:
# European format: "1.234,56" → 1234.56
value = "1.234,56"
value = value.replace(".", "").replace(",", ".")
number = float(value)
  • Check for multi-line strings. If your value contains embedded newlines, you may be passing multiple numbers as one string. Split first:
data = "42\n73\n91"
numbers = [int(line) for line in data.strip().split("\n")]
  • Use a linter or type checker. Tools like mypy or pyright can catch cases where a non-string type gets passed to int() before runtime. Run mypy your_script.py to find type mismatches early.

If none of these solve it, add a try/except ValueError block around the conversion, log the raw value with repr(), and inspect the actual data causing the failure. The error message always shows the offending value — use that to trace back to the source.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles