Python TypeError: unsupported operand type(s)
TypeError: unsupported operand type for +/- /...
Verified against CPython 3.13 source (Objects/abstract.c), Python docs: built-in-exceptions, Python docs: data-model (operator dispatch) · Updated April 2026
> quick_fix
Python's operators have type-specific behaviour. When you write a + b and Python can't find an __add__ method on a's type that accepts b's type (or a __radd__ on b's type that accepts a's), it raises TypeError. Convert one side to match the other - typically str(x) for concatenation or int(x) for arithmetic.
# Raises TypeError
age = 30
print('I am ' + age + ' years old')
# Fix - convert int to str
print('I am ' + str(age) + ' years old')
# Or use f-string
print(f'I am {age} years old')What causes this error
Python is dynamically typed but strict about operator semantics. Each type defines which other types it can be added to, multiplied with, compared against, etc. When the operator dispatch finds no matching __add__ or reflected __radd__, the TypeError is raised by the interpreter with a message naming the two unsupported types.
How to fix it
- 01
step 1
Read the full error message for the type names
TypeError messages include the operator and both type names (e.g., 'unsupported operand type(s) for +: int and str'). That tells you exactly which conversion to apply.
- 02
step 2
Convert types explicitly
For string concatenation, wrap numerics in str(). For arithmetic, convert strings via int() or float(). Be defensive about user input that might not parse.
- 03
step 3
Prefer f-strings for formatted output
f'value = {x}' calls __format__ on x, which works for any type without manual conversion. Cleaner than str(x) + ' = ' + str(y).
- 04
step 4
For collections, check the operation makes sense
list + tuple raises TypeError - both are sequences but Python won't auto-convert. Use list(t) or tuple(l) explicitly.
How to verify the fix
- The TypeError no longer fires on the offending line.
- Output values are the expected type (run type(result) to confirm).
- Edge cases (empty string, zero, None) handled correctly.
Why TypeError happens at the runtime level
Python's binary operators dispatch via the C-level type slots tp_as_number, tp_as_sequence, tp_as_mapping in Objects/abstract.c. For a + b, the interpreter calls PyNumber_Add which tries a.__add__(b) first; if that returns NotImplemented or the slot is null, it tries b.__radd__(a). If both fail, PyNumber_Add returns NULL and the interpreter raises TypeError with a formatted message naming both types. The dispatch is strict because Python's type system explicitly rejects implicit conversions to keep operator semantics predictable across types defined by libraries.
Common debug mistakes for TypeError
- Concatenating str() onto a list expecting str representation - 'prefix' + str([1,2,3]) gives 'prefix[1, 2, 3]' not 'prefix1, 2, 3', so you get TypeError elsewhere when consuming code expects a list.
- Using + to merge dicts in Python 3.8 and earlier - dict concatenation only works with the | operator from 3.9+; older versions raise TypeError.
- Comparing date and datetime with < or > - they have different __lt__ contracts and the comparison raises TypeError in 3.x (it silently succeeded in 2.x).
- Adding bytes and str - they're different types in Python 3 even though both look like text; use .encode() or .decode() to convert.
- Calling a method on None - the error is technically AttributeError but the root cause is often a TypeError from earlier (a function returned None when it should have returned an object).
When TypeError signals a deeper problem
Recurring TypeError across a codebase usually signals the project lacks runtime type validation at boundaries (HTTP request bodies, database rows, file parses). When inputs flow through ten functions without ever being type-checked, any one of them can hit TypeError with a useless stack trace pointing at the deepest call site rather than the entry point. The architectural fix is a typed boundary library (Pydantic, attrs, dataclasses with __post_init__) at every external input, with mypy or pyright enforcing types internally. Without it, every new field added to an API or database becomes a TypeError waiting to happen.
Frequently asked questions
Why doesn't Python auto-convert types like JavaScript?
By design. Implicit conversion in JS leads to bugs depending on order. Python requires explicit conversion so the developer's intent is clear.
What's the difference between TypeError and ValueError?
TypeError - the type itself is wrong (str + int). ValueError - the type is right but the specific value is invalid (int('abc') - str is the right type but 'abc' isn't parseable).
How do I catch TypeError gracefully?
Use try-except TypeError around the offending operation. But better: validate types up front with isinstance() or type hints + a runtime checker like Pydantic.