TypeError: unsupported operand type(s) for +: 'int' and 'str'
1 · The lesson
readWhat this error means
Python refuses to glue a number and a string together with +. The + operator means "add" for numbers and "concatenate" for strings, and Python won't guess which one you meant. Unlike JavaScript or PHP, Python is strongly typed: it will not silently convert 25 into "25" (or vice versa) just because you asked it to.
When you see it
Traceback (most recent call last): File "main.py", line 2, in <module> print("I am " + age + " years old") TypeError: can only concatenate str (not "int") to str
Or the reverse direction:
Traceback (most recent call last): File "main.py", line 2, in <module> total = 5 + "10" TypeError: unsupported operand type(s) for +: 'int' and 'str'
Why it happens
+ is overloaded. For int + int it adds. For str + str it concatenates. For int + str there is no sensible default — should 5 + "10" be 15 or "510"? Python refuses to choose and raises TypeError instead. Any value coming from input(), a CSV cell, or a JSON field is almost always a str, even when it looks like a number.
How to fix it
Cleanest fix — use an f-string:
age = 25 print(f"I am {age} years old")
Convert explicitly with str() or int():
age = 25 print("I am " + str(age) + " years old") # int -> str total = 5 + int("10") # str -> int
Or use .format:
print("I am {} years old".format(age))
setup added so this can run · defines age
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) age = _AutoMock('age')
Pick the conversion direction based on what the value means. If you need to do arithmetic, convert to int or float. If you just need to display it, convert to str (or use an f-string and skip the manual conversion entirely).
When you'd actually see this in real code
- Reading numeric input from
input()— it always returns astr, soinput("Age: ") + 1blows up. - Concatenating user IDs into a URL:
"/users/" + user_idwhenuser_idis anintfrom the database. - Building log lines:
"Processed " + count + " rows"whencountis a counter. - CSV columns are strings; trying to sum a column without
int()will fail at the first row.
Related errors
- TypeError: 'int' object is not subscriptable — another flavour of type confusion.
- TypeError: 'str' object does not support item assignment — strings are immutable.
- TypeError: list indices must be integers or slices, not str
See Also
- All Python errors — the full index, by type and by when it happens.
- F-Strings cheat sheet — the modern way to join strings and numbers.
- Variables (tutorial)
- Strings (tutorial)