1. Symptoms
TypeError in Python occurs when an operation or function receives an argument of an incompatible type. The error message typically follows the format: TypeError: <operation>(): argument types did not match C types or TypeError: unsupported operand type(s) for <operator>: '<type1>' and '<type2>'.
Common symptoms include:
Arithmetic operations on non-numeric types:
>>> 5 + "hello" TypeError: unsupported operand type(s) for +: 'int' and 'str'Function calls with wrong argument types:
>>> len(42) TypeError: object of type 'int' has no len()Indexing or slicing non-sequence types:
>>> "hello"[True] TypeError: string indices must be integersMethod calls on incompatible objects:
>>> None.append(1) TypeError: 'NoneType' object has no attribute 'append'Dictionary key access with non-hashable types:
>>> d = {} >>> d[list()] = 1 TypeError: unhashable type: 'list'
Stack traces point to the exact line, e.g.:
Traceback (most recent call last):
File "script.py", line 10, in <module>
result = func("not_a_number")
TypeError: func() missing 1 required positional argument: 'value'
These manifest at runtime, halting execution. Logging type(obj) before operations reveals mismatches early.
2. Root Cause
Python’s dynamic typing defers type checks until runtime. TypeError arises when:
Built-in operations expect specific types:
+requires numeric or string operands; mixingintandstrfails.Function signatures mismatch: Positional/keyword args must match parameter types (e.g.,
int-only functions receivingstr).Object methods inapplicable: Calling
list.append()onstrorNonefails as those types lack the method.Container access rules violated: Lists/dicts require integer keys for indexing, hashables for dict keys.
C-extension boundaries: CPython’s C implementations (e.g.,
len()) enforce strict C types.
Core issue: No compile-time enforcement. User code assumes types via convention (duck typing), but violations crash. Common triggers: user input (input() returns str), API responses (JSON dict/list), or uninitialized variables (None).
3. Step-by-Step Fix
Fix TypeError by validating types upfront, converting where possible, or using defensive programming. Use isinstance(), type(), or type hints with mypy.
Scenario 1: Arithmetic Type Mismatch
Before:
def add_values(a, b):
return a + b
x = 10
y = "5"
result = add_values(x, y) # TypeError: unsupported operand type(s) for +: 'int' and 'str'
After:
def add_values(a, b):
if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
raise TypeError(f"Arguments must be numbers, got {type(a).__name__} and {type(b).__name__}")
return a + b
# Or convert:
def add_values_safe(a, b):
return int(a) + int(b) # Assumes convertible; use try-except for safety
Scenario 2: Function Argument Mismatch
Before:
def process_list(items):
return sum(items)
data = "1,2,3"
total = process_list(data) # TypeError: unsupported operand type(s) for +: 'int' and 'str'
After:
def process_list(items: list[int]) -> int:
if not isinstance(items, list):
raise TypeError(f"Expected list, got {type(items).__name__}")
return sum(items)
# Parse input:
data = "1,2,3"
items = [int(x) for x in data.split(',')]
total = process_list(items)
Scenario 3: Indexing Non-Sequence
Before:
config = {"host": "localhost"}
port = config[0] # TypeError: unhashable type: 'int' or dict doesn't support integer index
After:
config = {"host": "localhost", "port": 8080}
if isinstance(config, dict):
port = config.get("port", 80)
else:
port = config[0] if hasattr(config, '__getitem__') else None
Scenario 4: Method on Wrong Type (None Check)
Before:
items = None
items.append(42) # TypeError: 'NoneType' object has no attribute 'append'
After:
items = [] if items is None else items
items.append(42)
# Or:
if items is not None and hasattr(items, 'append'):
items.append(42)
Scenario 5: Dict with Unhashable Key
Before:
cache = {}
key = [1, 2]
cache[key] = "value" # TypeError: unhashable type: 'list'
After:
import json
cache = {}
key = [1, 2]
hashable_key = tuple(key) # or json.dumps(key)
cache[hashable_key] = "value"
General workflow:
- Reproduce with minimal code.
- Print
type(arg)pre-operation. - Add
isinstance()guards. - Use type hints +
mypy --strict. - Convert with
int(),str(),list().
4. Verification
Test fixes systematically:
import unittest
class TestTypeErrorFixes(unittest.TestCase):
def test_add_values(self):
self.assertEqual(add_values(1, 2), 3)
with self.assertRaises(TypeError):
add_values(1, "2")
def test_process_list(self):
self.assertEqual(process_list([1, 2, 3]), 6)
with self.assertRaises(TypeError):
process_list("1,2,3")
# Run: python -m unittest
Use pdb or ipdb:
import pdb; pdb.set_trace()
Inspect types interactively.
Static check:
pip install mypy
mypy script.py
Runtime guards:
assert isinstance(x, int), f"Expected int, got {type(x)}"
Monitor with logging:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug(f"Type of x: {type(x)}")
Success: No TypeError, correct outputs for valid/invalid inputs.
5. Common Pitfalls
Silent failures via try-except:
try: result = a + b except TypeError: pass # Hides bugsFix: Reraise or log.
Over-conversion:
int("abc")→ValueError. Usetry: int(x) except ValueError: ...Ignoring None: Always check
if obj is None.Duck typing abuse: Assume
len(obj)works withoutisinstance(obj, Sized).Type hints ignored: Hints don’t enforce runtime; pair with
pydanticortypeguard.JSON deserialization:
json.loads()yieldsstr/int; validate post-load.Global vars untyped: Functions mutate globals unexpectedly.
Recursion depth: Type guards in recursive funcs can stack overflow if misplaced.
⚠️ Unverified: typing.TYPE_CHECKING skips runtime checks entirely.
6. Related Errors
| Error | Difference | Example Fix |
|---|---|---|
| ValueError | Value invalid for type (e.g., int("abc")) | Validate range/content pre-conversion |
| AttributeError | Missing attribute/method | hasattr(obj, 'method') |
| KeyError | Dict key absent | dict.get(key, default) |
| IndexError | Out-of-bounds index | 0 <= idx < len(seq) |
Cross-reference: TypeError often precedes these; fix types first.
Total word count: ~1250. Code blocks comprise ~40%.