Your script crashes, and near the bottom of the traceback sits AttributeError: 'NoneType' object has no attribute 'name'. It reads like Python is being deliberately unhelpful — but it's actually telling you something precise. You just tried to use a variable that turned out to be None, and it's telling you exactly which one and where.
The error isn't saying your program is fundamentally broken. It's saying: at this exact line, you reached for an attribute on a value that was None instead of the object you expected. That's a narrow claim, and once you know how to read it, tracking down why it was None is usually mechanical.
What the error is actually telling you
Take this code:
class User:
def __init__(self, id, name):
self.id = id
self.name = name
def find_user(users, user_id):
for u in users:
if u.id == user_id:
return u
return None
user = find_user(users, target_id)
print(user.name)
# AttributeError: 'NoneType' object has no attribute 'name'
Read the message in two parts. 'NoneType' object has no attribute 'name' tells you the object you called .name on wasn't a User — it was None. has no attribute 'name' tells you which access failed. Put together: whatever user was pointing to when you hit that line wasn't what you expected — it was nothing at all.
The message never claims .name is the problem. .name is just where the crash became visible. The real question is one step earlier: why was user None? Here, find_user() falls through its loop without a match and explicitly returns None — so either target_id is wrong, or that user genuinely isn't in the list yet.
The fix, step by step
-
Read the attribute name in the error (
'name'here) — that tells you which line and which access failed, nothing more. -
Trace back to where the
Nonevalue came from. Find the line that assigned, returned, or fetched it. -
Ask why it's
Nonethere, specifically. The most common causes: a lookup function that found nothing and returnedNone, adict.get()call that didn't find the key, or — easy to miss — a function with a code path that falls off the end without hitting areturnat all. Python returnsNoneimplicitly in that case, silently. -
Fix the actual cause, not just the crash site. If the value can legitimately be missing, check for
Nonedeliberately before using it. If it should never be missing, the bug is upstream — a typo in the lookup key, a branch that forgot to return, or a wrong assumption about what the data contains. -
Confirm with
print()andtype()on the variable itself, right before the crashing line, before you change anything.
Two mistakes worth knowing about ahead of time
Reaching for getattr(user, "name", None) as a reflex instead of a decision. It's Python's version of the same instinct that reaches for ?. in JavaScript — it makes the crash go away without answering why user was None in the first place. Sometimes that's correct, because the data really is optional. Other times it quietly papers over a real bug, and the missing value just surfaces somewhere else later, harder to trace.
Assuming the crash line is where the bug lives. user was already None before print(user.name) ever ran — the crash just shows up wherever the property access happens, not wherever the value went wrong. A version of this that catches people off guard: a function with an early if branch that returns a value, and a later path that falls off the end with no return at all. That path doesn't error where the bug is — it errors wherever the caller next tries to use the result.
A debugging habit that works
Before changing anything, print() the variable itself, one line above the crash — not the attribute, the whole object — and check its type(). If it's None, walk backward: where was it supposed to be set, and does every path through that function actually return something? A missing return on one branch is the single most common real-world cause behind this exact error, and it's invisible until you go looking for it, because Python never complains at the point you forgot to write it.
Once you know why it's None, the fix is one of two things: guard for it on purpose, with a clear fallback or an explicit "not found" path, or fix the function that's silently swallowing a code path it should have returned from. Both are valid — just make sure you know which one you're doing before you reach for getattr() and move on.
This post is adapted from the Python Essentials Companion Guide — a practical, no-fluff reference built for developers who want to actually understand Python, not just copy syntax.
Top comments (0)