Debugging — Python Error Guide

NoneType Error —
The silent bug that crashes somewhere else entirely.

A function returned None when you expected a real value — and nothing crashed at that moment. None traveled quietly through your program, sometimes through several more lines or function calls, until it finally hit an operation that doesn't know what to do with it. Watch None propagate on LearnBug and see the gap between where it originates and where it finally crashes.

NoneTypeThe type behind it
DelayedCrash location
PythonLanguage

What is a "NoneType Error"?

Not a single specific exception — it's a family of errors (AttributeError, TypeError) that all trace back to the same root cause: a variable that's unexpectedly None. Every Python function that doesn't explicitly return a value returns None by default, which is easy to forget.

Why visualization helps

This is the hardest error family to debug from a traceback alone, because the crash line is rarely the bug's actual location — the real mistake is upstream, wherever None first got created. LearnBug lets you trace a value backward through the execution, so you can find the actual origin instead of just the symptom.

LearnBug — None traveling from where it's created to where it crashes
🖼 Add a LearnBug screenshot here
What Causes It

Three ways None sneaks into your code

A function with no return on some path

A function that returns a value inside an if-branch, but falls through to the end (implicitly returning None) if that condition is never met.

Missing return

A "find" operation that finds nothing

dict.get(), re.match(), or a custom search function all return None when the search comes up empty — perfectly normal, but easy to forget to check for.

Empty search

A method that mutates in place and returns None

my_list.sort() and list.append() both return None — writing x = my_list.sort() assigns None to x, not the sorted list.

In-place methods
Walkthrough

A search function with no matching return

1

get_first_even([1, 3, 5]) loops through, checking each for evenness

1, 3, and 5 are all odd — the if n % 2 == 0 condition never fires for any of them, so the explicit return n line never executes.

1odd
3odd
5odd
Checked all three: none are even — the loop finishes with no match
2

The loop ends. The function has no code after it — implicitly returns None

This is the part that's easy to miss reading the source: there's no explicit return None anywhere, but falling off the end of a function is exactly equivalent to one.

implicit return Noneno error here
get_first_even([1,3,5]) → None — no exception raised at this point
3

result = None. Two lines later, result * 2 finally crashes

The assignment result = get_first_even(...) succeeds fine — it's a valid None assignment. The crash only happens when result is actually used in an operation that doesn't support NoneType.

Result: TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' ✗ — two lines away from the real cause

Code Example

The broken code, and the traceback it produces

PythonBroken — no return for the "not found" case
def get_first_even(nums):
    for n in nums:
        if n % 2 == 0:
            return n
    # no return here if no even number is found — implicitly returns None

result = get_first_even([1, 3, 5])
print(result * 2)
TracebackWhat Python actually prints — note the line number
Traceback (most recent call last):
  File "evens.py", line 8, in <module>
    print(result * 2)
          ~~~~~~~^~~
TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
The Fix

Handle the "not found" case explicitly

PythonFixed — check the result before using it
def get_first_even(nums):
    for n in nums:
        if n % 2 == 0:
            return n
    return None   # explicit — makes the "not found" case visible in the code

result = get_first_even([1, 3, 5])
if result is not None:
    print(result * 2)
else:
    print("No even number found")

Quick Reference

Root typeNoneTypeEvery function that doesn't explicitly return a value returns None
Typical causeIncomplete return pathsA function that only returns inside a conditional, with nothing for the "else" case
Fix patternExplicit return + checkReturn None deliberately, and check for it immediately at the call site
Debugging tipTrace backward from the crashThe crash line is rarely the bug — walk back to where the value first became None

Frequently asked questions

Why did Python let result = None succeed with no error at all?

Because assignment itself never validates the value being assigned — None is a completely valid Python value, just like any other. The error only appears once you try to do something with result that None doesn't support, which can be lines, or even function calls, away from the assignment.

What's a good habit to avoid this class of bug entirely?

Make every code path in a function explicit about what it returns — including an explicit return None for the "nothing found" case, rather than letting the function fall off the end silently. It doesn't change behavior, but it makes the possibility obvious to anyone reading the function later.

Does list.sort() returning None trip people up specifically?

Very often — my_list.sort() sorts the list in place and returns None, so writing sorted_list = my_list.sort() assigns None, not the sorted list. The list itself was correctly sorted; the mistake is expecting the method's return value to be it. Use sorted(my_list) instead if you want a new sorted list as a return value.

How do I trace where a None value actually originated?

Work backward from the crash: identify the variable that's None, find the line that assigned it, and check whether that line's source (a function call, a dict lookup, a search) could return None. Repeat until you find the actual source. LearnBug's step-by-step execution makes this concrete instead of requiring you to trace it by hand.

Watch None travel from where it's created to where it crashes

Paste your code into LearnBug and trace the real origin, not just the symptom.

Open Playground →