pythonseverity: can-fix
TypeError

Python TypeError: unsupported operand type(s)

TypeError: unsupported operand type for +/- /...

96% fixable~5 mindifficulty: beginner

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.

> advertisementAdSense placeholder

How to fix it

  1. 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.

  2. 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.

  3. 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).

  4. 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.

Editor's take

This error has a particular talent for surfacing in staging pipelines right before a freeze window. The classic scenario: a small product team is running a FastAPI service on Python 3.12, a junior dev patches a Pydantic v2 model to return a Decimal instead of float, and three services downstream start exploding at midnight because something is trying to add that Decimal to a plain int from a Redis cache hit. The Redis layer returns bytes by default unless you set decode_responses=True — so suddenly you have bytes + int, and the TypeError surfaces in an aggregation function nobody has touched in eight months.

Fixing a TypeError quickly is table stakes; understanding why it appeared reveals where you actually are in your Python maturity. First-month engineers reach for str() or int() casts and move on. Engineers ready for a mid-level bump start asking why the type was wrong at that point — which usually means tracing back to a JSON deserializer, a database cursor returning Decimal from psycopg3, or a Pydantic field without strict=True. The ability to read a traceback, find the boundary where the type contract was broken, and add a validator there rather than a cast downstream is the actual skill being tested.

When you see this error in production, check the surrounding traceback for AttributeError: 'NoneType' object has no attribute — it usually means a prior lookup returned None that got passed onward untouched. You will also frequently find ValueError: invalid literal for int() with base 10 upstream, indicating a string that was never cleanly parsed before arithmetic. If the service uses SQLAlchemy 2.x, watch for MappingProxyType comparison errors in the same incident caused by ORM row objects being compared directly with == to raw dicts.

By Bikram Nath · Curator · Updated April 2026

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.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is cross-referenced against the official sources listed in the “sources” sidebar before it ships. If a fix here didn’t work for you, please email so we can update the page.