Python • Syntax and Core Types

Understanding TypeErrors — Explain common TypeError causes and fixes (conceptual)

Learn why Python raises TypeErrors, what causes them, and how to fix them confidently in your code.

Code braces with terminal and markup symbols representing Python programming.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

When you first start programming in Python, it doesn't take long before you encounter a scary-looking red message called a TypeError. This error has been part of Python since the language's very first release, because Python's creator, Guido van Rossum, designed the language around a core idea: every piece of data has a type, and certain operations only make sense for certain types. The TypeError is Python's way of protecting you from writing code that mixes types in ways that don't logically work, like trying to add a number to a word.

1991 • Python 0.9 Released

Guido van Rossum releases the first version of Python. Even this early version enforces type rules, raising errors when operations are applied to incompatible data types.

2000 • Python 2.0 and Dynamic Typing

Python 2.0 popularizes dynamic typing — variables don't declare a type, but the values they hold still have one. This flexibility makes TypeErrors more common for beginners.

2008 • Python 3.0 Tightens Type Rules

Python 3 removes many implicit type conversions. For example, mixing strings and integers in concatenation now always raises a TypeError instead of silently guessing.

2015 • Type Hints Introduced (PEP 484)

Python 3.5 adds optional type hints, letting programmers annotate expected types. Tools like mypy can now catch potential TypeErrors before code even runs.

So the fundamental question this lesson addresses is: Why does Python refuse to run certain operations, what patterns trigger a TypeError, and how do you fix them? Understanding the answers will make you a much more confident debugger and programmer.

SECTION 2

Core Principles & Definitions

Before diving into specific examples, you need to understand a few foundational ideas about how Python thinks about types. A TypeError is an exception that Python raises whenever an operation or function receives a value of the wrong type. Python is dynamically typed — meaning you don't declare types for variables — but it is also strongly typed, meaning it won't secretly convert one type to another when the conversion might not make sense.

1

Every Value Has a Type

In Python, 42 is an int, "hello" is a str, and [1, 2] is a list. Use type() to check any value's type.
2

Operators Are Type-Sensitive

The + operator adds two numbers OR concatenates two strings, but it cannot mix them. Each operator defines which type combinations it accepts.
3

Python Won't Guess for You

Unlike some languages, Python refuses to automatically convert "5" to 5. You must explicitly convert types using functions like int() or str().
4

Functions Expect Specific Types

Built-in functions like len() only work on sequences (strings, lists, etc.). Passing an integer to len() triggers a TypeError because integers don't have a length.
5

Callability Matters

Only functions and classes can be "called" with parentheses. Writing x = 10; x() raises a TypeError because an integer is not callable — you can't execute a number like a function.
✦

KEY TAKEAWAY

Think of Python types like electrical plugs and outlets. A USB-C plug fits a USB-C port, and an HDMI plug fits an HDMI port. If you try to jam a USB-C plug into an HDMI port, it just won't work — and Python's TypeError is telling you exactly that: you're trying to connect two things that aren't compatible. You need an adapter (type conversion) or you need to use the right plug in the first place.

SECTION 3

Visual Explanation — Anatomy of a TypeError

When Python raises a TypeError, it gives you a traceback — a structured message that tells you exactly what went wrong and where. The diagram below breaks down the anatomy of a typical TypeError message so you know how to read every part of it.

Anatomy of a TypeError TracebackTraceback (most recent call last): File "main.py", line 3, in <module> result = "Score: " + 42TypeError: can only concatenate str (not "int") to str① Error Type"TypeError" — tells youit's a type mismatch② LocationFile name + line numberwhere the error happened③ The Offending LineThe exact code thatcaused the error④ Error MessageDescribes the specific mismatch:str + int is not allowed⑤ The FixConvert int to str first:"Score: " + str(42)Read the traceback from bottom to top: the error message is the most important part.
This diagram shows the five key parts of a TypeError traceback. Start by reading the error message at the bottom, which tells you exactly what types clashed. Then look at the location to find which line to fix.

The most useful habit you can build is to always read the last line of the traceback first. That line contains the error type (TypeError) followed by a human-readable description of what went wrong. Once you understand the message, look at the line above it to find which exact line of your code triggered the problem.

SECTION 4

How Python's Type System Works

Python uses a concept called duck typing — if it walks like a duck and quacks like a duck, Python treats it like a duck. In practice, this means Python doesn't check the type of a variable when you assign it. Instead, it checks at runtime — the moment your code actually tries to perform an operation. If the types don't support that operation, Python raises a TypeError right then and there.

The Type-Checking Decision Process

When Python encounters an expression like a + b, it performs an internal check. First it asks: does the type of a define how to add itself to the type of b? If a is a string and b is also a string, the + operator concatenates them. If a is a string and b is an integer, Python finds no matching rule and raises a TypeError. This happens entirely at runtime, which is why you might not discover a TypeError until your program actually reaches that line.

How Python Evaluates a + b at Runtimea + bCheck type(a) and type(b)Does type(a) define + for type(b)?YESNO✓ Operation succeedsResult is returned✗ TypeError raisedProgram stops (unless caught)Examples that succeed:3 + 4 → 7"hi" + " there" → "hi there"Examples that fail:"Score: " + 42 → TypeError"5" - 3 → TypeError
This flowchart shows Python's decision process when evaluating an operation. Python checks whether the types involved support the requested operation. If they do, the operation succeeds. If not, a TypeError is raised and the program stops.
ℹ

Dynamic vs. Static Typing

Languages like Java and C++ are statically typed — the compiler catches type errors before the program even runs. Python is dynamically typed, so TypeErrors only appear when the offending line is actually executed. This means a TypeError can hide inside an if branch you rarely reach — and surprise you later!

SECTION 5

Common TypeError Causes & Their Fixes

While there are many situations that can trigger a TypeError, most beginner errors fall into a handful of predictable categories. The table below catalogs the most common patterns you'll encounter, along with an example of the broken code and the fix for each one.

CategoryBroken Code ExampleError Message (abbreviated)Fix
String + Number"Age: " + 17can only concatenate str (not "int") to str"Age: " + str(17)
Calling a Non-Functionx = 5; x()'int' object is not callableRemove the parentheses or use the correct function name
Wrong Argument Countdef greet(name): ...; greet()greet() missing 1 required positional argument: 'name'Pass the required argument: greet("Alice")
Subscripting Nonex = [1,2].sort(); x[0]'NoneType' object is not subscriptableRemember that .sort() returns None; sort first, then index
Iterating a Non-Iterablefor i in 42: print(i)'int' object is not iterableUse range(42) or wrap in a list
Unsupported Operand Types"5" - 3unsupported operand type(s) for -: 'str' and 'int'Convert first: int("5") - 3

The six most common TypeError patterns in beginner Python code

Notice a pattern in the fixes: most TypeErrors are solved either by converting a value to the expected type (using str(), int(), float()) or by using the correct operation for the type you actually have. The error message itself almost always tells you both which types clashed and what operation triggered the conflict.

SECTION 6

Worked Example — Debugging a TypeError Step by Step

Let's walk through a realistic scenario. Imagine you wrote a program that asks the user for their birth year and then calculates their age. When you run it, Python throws a TypeError. Here's how to debug it systematically.

Debugging a User Input TypeError

  1. 1

    Step 1 — Identify the Broken Code

    Here is the code that causes the error: birth_year = input("Enter your birth year: ") followed by age = 2025 - birth_year. When you run this and enter 2007, Python raises: TypeError: unsupported operand type(s) for -: 'int' and 'str'
  2. 2

    Step 2 — Read the Error Message

    The message tells us that the - operator received an int on the left (2025) and a str on the right (birth_year). You can't subtract a string from a number.
    Root cause identified: input() always returns a string, even if the user types a number.
  3. 3

    Step 3 — Verify with type()

    Add a debug line: print(type(birth_year)). This will output <class 'str'>, confirming our diagnosis. The variable holds the string "2007", not the integer 2007.
  4. 4

    Step 4 — Apply the Fix

    Wrap the input() call with int() to convert the string to an integer: birth_year = int(input("Enter your birth year: ")). Now 2025 - birth_year is int - int, which Python handles perfectly.
    Fixed! The program now correctly computes age = 2025 - 2007 → 18
  5. 5

    Step 5 — Generalize the Lesson

    Any time you use input() and need to do math with the result, you must convert the return value to a numeric type first. This is one of the most common sources of TypeErrors in Python programs written by beginners.

SECTION 7

Strategies for Fixing and Preventing TypeErrors

Knowing what causes TypeErrors is only half the battle. The other half is building habits that prevent them from happening in the first place — or at least help you fix them quickly when they do appear.

StrategyWhat It DoesExample
Use type() to inspectCheck the actual type of a variable when you're unsure. Great for debugging.print(type(my_var))
Explicit type conversionConvert values to the right type before operating on them.int(x), str(x), float(x)
f-strings for outputAutomatically convert values inside strings, avoiding str + int errors entirely.f"Age: {age}"
isinstance() checksTest whether a value is the expected type before using it in an operation.isinstance(x, int)
Read return values carefullyMany methods (like .sort() and .append()) return None. Don't assign their result to a variable you plan to use.Call my_list.sort() on its own line, then use my_list

Five strategies for preventing and fixing TypeErrors

✦

KEY TAKEAWAY

Think of f-strings as the Swiss Army knife for avoiding TypeErrors in print statements. Instead of manually converting every variable with str(), just write f"The answer is {my_var}" and Python handles the conversion for you. It's like having a universal adapter that fits every type of plug.

SECTION 8

TypeErrors in the Bigger Picture

TypeErrors are just one member of Python's error family. As you grow as a programmer, you'll learn to distinguish between several kinds of exceptions that look similar but have different causes. The table below shows how a TypeError compares to other common Python errors.

Error TypeWhen It HappensExample
TypeErrorAn operation receives the wrong type of value."hello" + 5
ValueErrorThe type is correct, but the value doesn't make sense.int("abc")
NameErrorYou used a variable name that doesn't exist yet.print(score) (never defined)
AttributeErrorYou tried to use a method or property that doesn't exist on that type.(5).upper()
IndexErrorYou accessed a list index that's out of range.[1, 2][5]

How TypeErrors compare to other common Python exceptions

As you advance, you'll learn to use try/except blocks to catch TypeErrors (and other exceptions) gracefully so your program doesn't crash. You'll also encounter type hints (annotations like def greet(name: str) -> str:) that let tools like mypy catch potential TypeErrors before you even run your code. These are powerful tools in your debugging toolkit, and they build directly on the concepts you've learned here.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why Python raises a TypeError when you write "Total: " + 100. Why doesn't Python just convert 100 to "100" automatically?
Show answer
Python is strongly typed, meaning it does not perform implicit (automatic) type conversions when they could be ambiguous. When Python sees "Total: " + 100, it doesn't know whether you intended to convert the integer to a string for concatenation, or whether you made a logic mistake. Rather than guessing and potentially producing wrong results silently, Python raises a TypeError to force you to be explicit. The fix is to write "Total: " + str(100) or use an f-string like f"Total: {100}".

Question 2: BASIC CALCULATION

A student writes the following code: price = input("Enter price: ") then total = price * 1.08. What error will Python raise, and how do you fix it?
Show answer
Python will raise TypeError: can't multiply sequence by non-int of type 'float'. The input() function always returns a string. When you multiply a string by a float, Python doesn't know how to handle it (string repetition only works with integers). The fix is to convert the input to a float first: price = float(input("Enter price: ")). Now price * 1.08 is float × float, which Python can compute.

Question 3: INTERMEDIATE

The following code crashes. Identify the line that causes the TypeError, explain why, and fix it: scores = [88, 92, 75, 90] sorted_scores = scores.sort() print("Top score: " + sorted_scores[-1])
Show answer
There are actually two problems. First, scores.sort() sorts the list in place and returns None, so sorted_scores is None. Trying to index None with [-1] raises TypeError: 'NoneType' object is not subscriptable. Second, even after fixing that, concatenating a string with an integer would also be a TypeError. Fix: call scores.sort() on its own line, then use print(f"Top score: {scores[-1]}").

Question 4: APPLIED

You're building a grade calculator. The user enters comma-separated scores as a single string: data = "85,90,78,92". A classmate writes average = sum(data.split(",")) / len(data.split(",")). What TypeError will occur and how do you fix it?
Show answer
The data.split(",") call produces ["85", "90", "78", "92"] — a list of strings, not integers. Passing this to sum() raises TypeError: unsupported operand type(s) for +: 'int' and 'str' because sum() tries to add strings together starting from 0. The fix is to convert each element to an integer first: scores = [int(s) for s in data.split(",")] and then average = sum(scores) / len(scores). This list comprehension ensures every element is an integer before summing.

Question 5: CRITICAL THINKING

Consider this code: result = "hello" * 3 — this works and produces "hellohellohello". But result = "hello" * 3.0 raises a TypeError. Both 3 and 3.0 represent the same mathematical value. Why does Python allow one but not the other? What does this tell you about how Python thinks about types?
Show answer
Python's string repetition operator * is specifically defined to work with integers because repeating a string a fractional number of times doesn't make logical sense — what would "hello" × 2.7 mean? Even though 3.0 has the same mathematical value as 3, Python checks the type, not just the value. 3 is an int and 3.0 is a float, and the string repetition rule only accepts integers. This illustrates that Python's type system is based on the data type of the value, not its numeric equivalence. You could fix this with "hello" * int(3.0).

SUMMARY

Summary

Summary

A TypeError occurs whenever Python encounters an operation applied to a value of the wrong type. The most common causes include mixing strings and numbers with operators like + or -, calling a non-callable object, passing the wrong number of arguments to a function, indexing a None value, and iterating over a non-iterable.

To fix TypeErrors, use explicit type conversion functions like int(), str(), and float(). To prevent them, adopt habits like using f-strings for string formatting, checking types with type() during debugging, and always remembering that input() returns a string. Always read the traceback from bottom to top — the last line contains the error message that tells you exactly which types conflicted.

Varsity Tutors • Python • Understanding TypeErrors