TypeError: can only concatenate str (not "int") to str occurs when the + operator receives a string and an integer in a text-concatenation expression. Python does not guess whether the intended result is text such as "Age: 30" or arithmetic such as 30 + 5. Convert at the boundary that matches the real intent.
Quick answer
For text, use str(value) or an f-string. For quick diagnostic output, pass separate values to print(). For arithmetic, convert a numeric string with int() or float() before adding. The Python documentation for the str type and str() explains the conversion boundary.

Why string plus integer fails
The expression below combines two different types. The string + operation requires another string, so Python raises TypeError instead of silently changing the integer.
age = 30
message = "Age: " + age
print(message)
This behavior prevents an ambiguous conversion. A value can be displayed, serialized, or added mathematically, and those operations have different meanings.

Convert the value with str()
Use str() when explicit concatenation is the intended output.
age = 30
message = "Age: " + str(age)
print(message)
str() creates a text representation. It does not change the original integer, and it should not be used when the next operation still needs numeric behavior.
Prefer an f-string for formatted text
F-strings are usually easier to read when a message contains more than one value. They also support format specifications for numbers, dates, and alignment.
name = "Asha"
score = 95
message = f"{name} scored {score}"
print(message)
Use str.format() or a template system when the project already standardizes on them. The important part is that formatting, rather than accidental concatenation, defines the conversion.
Use print arguments for quick output
If you only need to inspect values while debugging, pass them as separate arguments. print() converts each argument for display and separates them with spaces by default.
name = "Asha"
score = 95
print(name, score)
Use an f-string when the exact message is part of a log format, test assertion, API response, or user interface. The default spacing from print() may not be the contract you need.

Convert input before doing math
input() returns a string. If a value came from user input or a text file and represents a number, parse it before arithmetic.
quantity_text = "4"
price = 5
total = int(quantity_text) + price
print(total)
Use float() for decimal input and validate or catch ValueError when the source may contain non-numeric text. Do not convert the number to text just to make the error disappear if the next operation is mathematical.
Fix join() with mixed values
str.join() also requires every item to be a string. Convert each value as part of the iterable passed to join().
values = ["A", 1, "B", 2]
joined = ", ".join(str(value) for value in values)
print(joined)
This keeps the original list values unchanged and makes the conversion local to the serialization step.

Check the data boundary
When the same value flows through input, computation, and display, decide where its type should change. Parse at input, keep numbers numeric during computation, and format at output. That separation avoids repeated conversions and makes type errors easier to diagnose.
Format numbers without losing meaning
F-strings let you control how a number is displayed without changing its stored type. Use a precision specifier for a measurement, a comma separator for a large count, or a conversion such as !r when the representation is useful for debugging.
temperature = 21.4567
visits = 12000
print(f"{temperature:.2f} C")
print(f"{visits:,} visits")
Formatting belongs at the presentation boundary. Do not store a formatted value in place of the numeric value when later code still needs sorting, arithmetic, aggregation, or range checks.
Handle None and other types explicitly
str() can represent None, booleans, lists, and custom objects, but a representation is not always a meaningful user-facing value. Decide whether a missing value should be omitted, replaced with a label, or rejected before formatting.
nickname = None
display_name = nickname if nickname is not None else "anonymous"
message = f"User: {display_name}"
print(message)
This policy is clearer than producing a message that accidentally says None. The same principle applies to values parsed from JSON, database rows, and optional command-line arguments.

Keep logging and serialization predictable
For structured logs, prefer the logging library’s arguments or a structured serializer rather than building a long string with repeated + operations. Structured fields preserve types for the log collector and avoid accidental delimiter or escaping bugs. For JSON, serialize the data as JSON rather than relying on str() to create a machine-readable document.
When an error happens inside a formatting expression, inspect the type as well as the value. A value that looks numeric may still be a string, decimal, date, or custom object. Type checks should support a clear contract, not replace one.
Debug mixed-type expressions
Break a complex expression into named values and inspect their types. Look backward to the boundary where the unexpected type entered the program. If the input contract says a field is numeric, validate it once and keep the validated value numeric. If the contract says it is text, format it consistently at the output boundary.
raw_quantity = "4"
quantity = int(raw_quantity)
unit_price = 5
total = quantity * unit_price
message = f"Total: {total}"
print(message)
This design prevents a later concatenation error because each variable has one clear role. It also makes tests straightforward: test parsing separately from arithmetic and formatting.
Use repr() when debugging a value’s representation, especially when whitespace or escape characters may be involved; use str() for ordinary human-readable output. This distinction helps reveal why a value that looks numeric or empty in a terminal may still have a different type or content at the conversion boundary.
For related string work, see Python string length and checking whether a string is an integer.
Frequently Asked Questions
Why does Python not concatenate a string and an integer?
The string plus operator requires string operands, and Python does not guess whether the intended result is text or arithmetic.
How do I concatenate a string and integer in Python?
Use str(integer) or an f-string when building text, such as f”Age: {age}”.
How do I add a number read with input()?
Convert the input string with int() or float() before performing arithmetic and handle invalid input as needed.
How do I join a list containing integers?
Convert each item while joining, for example ‘, ‘.join(str(value) for value in values).