Fix Common Errors

'NoneType' Object Cannot Be Interpreted as an Integer

This Python TypeError means a function returned None where a number was expected — often range(), len(), or a method that mutates in place. Here's the fix.

TypeError: 'NoneType' object cannot be interpreted as an integer is a Python error that hits when you pass None to a function or operator that demands an actual integer — usually range(), list slicing, round(), or a numpy/pandas API. It's trickier than it sounds because the None usually didn't come from where you think. This guide walks through where the None actually comes from, how to spot it in a stack trace, and the defensive fixes that prevent it.

This error is closely related to other NoneType errors like "'NoneType' object is not iterable" and "'NoneType' object is not subscriptable", but the integer-specific case has a clear culprit: a function that mutates in place and returns None, and you assigned it thinking it returns a value.

What the error means

In Python, certain operations require an integer argument. range() takes start, stop, and step as integers. List slicing uses integers for indices. round() takes an optional precision integer. When you pass None to any of these, Python can't convert it to an integer and raises this error:

count = None
for i in range(count):  # TypeError: 'NoneType' object cannot be interpreted as an integer
    print(i)

The error message is literal: you gave Python None, and it tried to treat it as an integer for the operation, but None is not and cannot become an integer. The root cause, though, is almost always that some earlier function returned None when you expected a number.

The insidious culprit: functions that return None

The most common reason this error appears is that you assigned the result of a function that mutates in place and returns None. Python's standard library is full of these — the logic being that if a function modifies its argument directly, there's nothing useful to return. But that design choice means one line of careless code turns your variable into None.

The classic traps

List methods that return None:

# BROKEN: sort() returns None, doesn't return the sorted list
numbers = [3, 1, 2]
sorted_numbers = numbers.sort()
for i in range(len(sorted_numbers)):  # TypeError: 'NoneType' object cannot be interpreted as an integer
    print(sorted_numbers[i])

The fix: use sorted() which returns a new list, or sort in place then use the original:

# FIXED: sorted() returns a new sorted list
numbers = [3, 1, 2]
sorted_numbers = sorted(numbers)
for i in range(len(sorted_numbers)):
    print(sorted_numbers[i])

The same trap hits with append(), extend(), and reverse():

# BROKEN
my_list = [1, 2, 3]
extended = my_list.extend([4, 5])
size = len(extended)  # TypeError: 'NoneType' object cannot be interpreted as an integer

# FIXED: extend() mutates in place, don't assign the result
my_list = [1, 2, 3]
my_list.extend([4, 5])
size = len(my_list)

Regex matches returning None:

# BROKEN: re.match() returns None if no match
import re
text = "hello world"
match = re.match(r"(\d+)", text)
count = range(match.start())  # AttributeError if match is None, or...
# If you later do: start_pos = match.group(1); print(range(start_pos))
# and group(1) is None (didn't match), you get the integer error

The fix: check for None:

# FIXED
import re
text = "hello world"
match = re.match(r"(\d+)", text)
if match:
    count = range(match.start())
else:
    count = range(0)

Dictionary lookups returning None:

# BROKEN: dict.get() returns None if the key doesn't exist
config = {"delay": 10}
repeat_count = config.get("attempts")  # None
for i in range(repeat_count):  # TypeError
    retry()

The fix: supply a default value to get():

# FIXED
config = {"delay": 10}
repeat_count = config.get("attempts", 0)  # Returns 0 if key missing
for i in range(repeat_count):
    retry()

Functions with no explicit return:

A function that doesn't return anything implicitly returns None. This is subtle:

def get_retry_count(user_id):
    if user_id < 0:
        return  # Returns None
    return 5

count = get_retry_count(-1)
for i in range(count):  # TypeError
    print(i)

The fix: return an explicit default in all branches:

def get_retry_count(user_id):
    if user_id < 0:
        return 0  # Explicit default
    return 5

count = get_retry_count(-1)
for i in range(count):
    print(i)

Assigning print() to a variable:

This one catches beginners. print() always returns None:

# BROKEN
result = print("Processing...")
for i in range(result):  # TypeError
    pass

The fix: don't assign print():

# FIXED
print("Processing...")
for i in range(10):
    pass

Where the error surfaces

The integer-required contexts where this error appears include:

  • range(n), range(start, stop), range(start, stop, step) — all three arguments must be integers or None raises this error.
  • List slicing: my_list[start:stop:step] — if any of start, stop, or step is None in a context expecting an integer, you'll see this.
  • round(x, ndigits) — if ndigits is None when the code expects an integer precision.
  • Numpy and pandas indexing: arr[n], df.iloc[n], or operations expecting integer arguments.

In NumPy and pandas, passing None to functions expecting integers often produces this exact error, but the traceback will point into their C extensions, making it harder to spot that your code passed None to them.

How to debug it

When this error appears in a stack trace, the hard part isn't understanding the error — it's finding which variable is None.

Read the stack trace carefully

The traceback shows the exact line where the operation failed:

  File "myapp.py", line 42, in process_order
    for i in range(result):
TypeError: 'NoneType' object cannot be interpreted as an integer

Now work backwards: result is None. Where did result come from? Look at the lines before:

result = get_count(order_id)  # <-- This returned None
for i in range(result):
    process(i)

Now look at get_count(): does it have a branch that returns without a value, or does it call a method that mutates in place?

Use print(repr(x)) or a debugger

Before the failing line, add:

print(repr(result))  # repr() always works, even on None

Or set a breakpoint and inspect the variable. repr(None) prints None plainly, whereas print(None) also prints None but could be confused with missing output.

Use type hints and a static checker

The most foolproof way to catch this before runtime: type your functions explicitly:

def get_count(order_id: int) -> int:
    if order_id < 0:
        return 0  # Type checker will complain if you return None here
    return db.count_for_order(order_id)

Run mypy or pyright on your code:

mypy myapp.py

A static type checker will catch most of these at analysis time, before you ship.

Defensive fixes

Explicit None checks

When a function might return None, check for it:

count = get_retry_count(user_id)
if count is None:
    count = 0  # Or raise an error
for i in range(count):
    retry()

Sensible defaults at the call site

Use the pattern x or default_value for simple cases, but be careful — this catches both None and other falsy values:

# Use this when None and 0 are equivalent
count = config.get("retries") or 1  # Returns 1 if retries is None or 0

But if you need to distinguish between None (missing key) and 0 (explicitly zero), use:

count = config.get("retries", 1)  # Returns 1 only if key is missing

Fix the source, don't hide the symptom

If a function returns None when it shouldn't, don't paper over it with or everywhere. Fix the function:

# BAD: masking the bug
result = do_work() or 0
for i in range(result):
    process(i)

# GOOD: fix do_work()
def do_work():
    # ... logic ...
    if failed:
        return 0  # Explicit default, not implicit None
    return count

Using or and defaults everywhere silences the error but lets broken functions ship. If a function is supposed to return an integer and returns None, that's a real bug — the function logic is broken. Don't hide it; fix it.

Spotting it in production

Locally you can add a breakpoint. In production you have a stack trace and no debugger. This is where error tracking saves hours: it captures the exact line that failed, the full stack trace, the breadcrumbs showing what led there, and the affected user count. Instead of guessing, you see: "this error hit 50 times today, all from users over 18–35 on the checkout page."

From there, the fix usually takes minutes. You look at the stack frame, trace backward to find which function returned None, and apply one of the defensive patterns above.

If you see other 'NoneType' errors like "'NoneType' object is not subscriptable" or "'NoneType' object has no attribute 'x'", they come from the same root cause: some function returned None when you expected a value. The debugging method is identical: read the traceback, work backward, fix the function or add a check. For more on those, see how to debug production errors.

Start tracking errors in minutes

Catch 'NoneType' errors before they hit production. Set up LightTrace with the Sentry SDK to capture the full stack trace, breadcrumbs, and user context for every TypeError in your Python app.

The good news: this error is almost entirely preventable. Use type hints, return explicit defaults instead of None, and check for None where a function might return it. Once these patterns are habit, you'll rarely see this error again.

Fix your next production error faster

Point any Sentry SDK at LightTrace — free up to 5,000 events/month.