Quick Answer

TypeError means an operation was applied to a value of the wrong type. The five patterns you will meet are: NoneType not subscriptable, when a function returned None; a str and int concatenation, usually because input() returns text; a missing positional argument, usually self; an object not callable, usually a name shadowed by a variable; and an unhashable type, when a list is used as a dict key. In each case the mistake is normally on an earlier line.

'NoneType' object is not subscriptable

Subscripting is the square-bracket operation, x[0] or x["name"]. None does not support it, so the moment a variable you expected to be a list or dict is actually None, this fires.

nums = [5, 2, 9]
nums = nums.sort()
print(nums[0])
# TypeError: 'NoneType' object is not subscriptable

The failing line is print(nums[0]), but the bug is the line above. list.sort() sorts the list in place and returns None, so assigning its result throws away your data. Use nums.sort() on its own line, or nums = sorted(nums) if you want a new list.

Python is consistent about this: methods that mutate an object return None. append, extend, reverse, insert, remove, dict.update, set.add and random.shuffle all behave the same way. Any line of the form x = x.something() where something mutates is silently destroying x.

The second source is a function that forgets to return on one path:

def find_student(rows, name):
    for r in rows:
        if r["name"] == name:
            return r
    # no return here: falls off the end and gives back None

s = find_student(students, "Asha")
print(s["city"])   # TypeError if Asha is not in rows

Note the related messages. 'NoneType' object is not iterable comes from looping over or unpacking None. 'NoneType' object has no attribute 'x' is an AttributeError, not a TypeError, but has the identical root cause. All three mean the same thing: trace backwards to find what produced None.

can only concatenate str (not "int") to str

Python will not guess what you meant by adding text to a number, so + across those types is an error rather than an automatic conversion. The single biggest source is input(), which always returns a string even when the user types digits.

age = input("Your age: ")     # "20", a string
print("Next year you are " + age + 1)
# TypeError: can only concatenate str (not "int") to str

The message depends on which operand is on the left, which trips people searching for the error text:

"Total: " + 199
# TypeError: can only concatenate str (not "int") to str

199 + " rupees"
# TypeError: unsupported operand type(s) for +: 'int' and 'str'

Two different messages, one cause. The fix is to convert deliberately, and f-strings are the cleanest way because they call str() for you on anything you interpolate:

age = int(input("Your age: "))
print(f"Next year you are {age + 1}")
print(f"Total: ₹{199 * 3}")

Be aware that int("20.5") raises ValueError, not TypeError, because the string is the right type but the wrong content. Use float() for decimals, and wrap conversions of user input in try/except ValueError if a bad entry should not crash the program.

The same rule extends beyond strings and numbers. [1, 2] + (3, 4) fails with can only concatenate list (not "tuple") to list, because + on sequences means join two of the same kind. And "Pune" * 3 works while "Pune" * "3" does not, which is a useful reminder that the operator is defined per type pair, not globally.

missing 1 required positional argument

This one means you called something with fewer arguments than its definition requires. When the missing argument is named self, the cause is almost always a method called on the class instead of an instance.

class Student:
    def __init__(self, name, city):
        self.name = name
        self.city = city

    def greet(self):
        print(f"{self.name} from {self.city}")

s = Student("Asha")
# TypeError: Student.__init__() missing 1 required positional argument: 'city'

Student.greet()
# TypeError: Student.greet() missing 1 required positional argument: 'self'

Newer Python versions prefix the qualified name, as above; older ones print just __init__(). Either way the argument name in quotes is the one you did not supply, and if that name is self you wrote ClassName.method() where you meant instance.method().

The mirror image reads oddly the first time you see it:

class Counter:
    def bump():        # self omitted from the definition
        print("bumped")

Counter().bump()
# TypeError: Counter.bump() takes 0 positional arguments but 1 was given

You passed nothing, yet Python says one argument was given. That is because calling a method on an instance passes the instance automatically as the first argument, and the definition has no parameter to receive it. Add self.

Outside classes, the same error usually means a changed function signature. If you add a parameter to a function and one call site is out of date, this is the message. Give new parameters a default value, def send(msg, retries=3), so existing calls keep working. Remember the ordering rules too: in a call, keyword arguments must come after positional ones, and in a definition, parameters that have defaults must come after those that do not.

'list' object is not callable

Calling means putting brackets after a name. This error says the thing you called is not a function, and by far the commonest cause is that you used a built-in name as a variable.

list = [1, 2, 3]          # list is now your list, not the built-in
nums = list(range(5))
# TypeError: 'list' object is not callable

The assignment succeeds silently, and the failure appears later at a line that looks perfectly normal. Names worth avoiding as variables include list, dict, str, int, set, type, id, sum, max, min, input, len and print. If a built-in mysteriously stops working, search your file for an assignment to that name. In a REPL session the shadowing survives until you restart, so a restart is the quickest test.

The second cause is round brackets where you needed square ones:

counts = {"pune": 3, "kochi": 1}
print(counts("pune"))
# TypeError: 'dict' object is not callable
print(counts["pune"])   # correct

The third is an omitted operator, which produces a message that looks unrelated to the real mistake:

a, b = 4, 5
total = 2(a + b)
# TypeError: 'int' object is not callable

Python read 2(...) as calling the integer 2. Write 2 * (a + b). The same shape appears with 'str' object is not callable when you write "Total"(x) instead of formatting, and with 'module' object is not callable when you run import datetime and then call datetime(2026, 1, 1), which calls the module rather than the datetime class inside it. Calling datetime.now() after that same import is a different mistake and raises AttributeError instead, because the module has no now on it.

unhashable type: 'list'

Dictionary keys and set members must be hashable, meaning Python can compute a stable number from their contents to decide where to store them. Lists are mutable, so their contents can change after storage and the hash would no longer match. Python refuses up front.

seen = set()
seen.add([1, 2])
# TypeError: unhashable type: 'list'

fares = {}
fares[["Pune", "Mumbai"]] = 400
# TypeError: unhashable type: 'list'

The fix is to use an immutable equivalent. Tuples are hashable as long as everything inside them is:

fares[("Pune", "Mumbai")] = 400     # fine
seen.add((1, 2))                    # fine

fares[("Pune", ["a"])] = 1
# TypeError: unhashable type: 'list'  (tuple containing a list)

For a set of sets, use frozenset, which is the immutable version of set. Dictionaries are also unhashable, so unhashable type: 'dict' means you tried to key one dictionary by another, usually while grouping JSON records. Key by a specific field, or by a tuple of the fields that identify the record.

One case catches people writing their own classes. Instances are hashable by default, based on identity. But defining __eq__ without __hash__ makes them unhashable, because Python cannot let two objects compare equal while hashing differently:

class Student:
    def __init__(self, roll):
        self.roll = roll
    def __eq__(self, other):
        return self.roll == other.roll

seen = {Student(1)}
# TypeError: unhashable type: 'Student'

Define __hash__ = lambda self: hash(self.roll) alongside it, or use @dataclass(frozen=True), which generates a matching pair for you.

Across all five patterns the debugging move is the same, and it is worth stating plainly. The traceback shows you where a wrong value was used, never where it was created. So do not stare at the failing line. Print the offending variable, and its type(), at each assignment above it until you find the first line where it stops being what you expected. That point is your bug.

Type hints help you find these before they run rather than after. Writing def find_student(rows: list, name: str) -> dict | None: documents that the function can return None, and a checker such as mypy will then flag every call site that indexes the result without checking. The dict | None spelling needs Python 3.10 or later; on older versions write Optional[dict] from typing instead. Hints change nothing at runtime, so they cannot break working code, but they turn a class of runtime TypeError into an editor warning you see while typing.

Frequently Asked Questions

What is the difference between TypeError and ValueError? TypeError means the type of the value is wrong for the operation, such as adding a string to an integer. ValueError means the type is right but the content is not usable, such as int("hello") where a string was expected but its contents are not a number. Knowing which you have tells you whether to convert the value or validate it.
Why does my sorted list become None? Because you wrote nums = nums.sort(). The sort method rearranges the list in place and returns None, so the assignment replaces your list with nothing. Call nums.sort() as a statement on its own line, or use nums = sorted(nums) when you want a new list and need to keep the original order intact.
Why does Python say a method takes 0 arguments but 1 was given? You defined the method without self. When you call a method on an instance, Python automatically passes that instance as the first argument, so a method defined with no parameters receives one it cannot accept. Add self as the first parameter of every instance method, and use the @staticmethod decorator when a method genuinely needs no instance.
Can I use a list as a dictionary key if I promise not to change it? No. Python enforces hashability at the type level rather than trusting a promise, because a changed key would land in the wrong bucket and the value would become unreachable. Convert the list to a tuple with tuple(my_list), or use a frozenset when order does not matter and the elements should be treated as a set.
How do I find the line that actually caused a NoneType TypeError? Stop guessing and use a debugger. Put breakpoint() a few lines above the crash, or run python -m pdb your_script.py, then step forward and watch the variable change. If the value comes from a function you wrote, adding assert result is not None immediately after the call converts a confusing TypeError further downstream into a failure at the exact line that produced the bad value.