Skip to main content

How to Resolve TypeError: expected string or bytes-like object in Python

The TypeError: expected string or bytes-like object is a frequent Python error. It arises when you pass an object of an incorrect type (like an integer, list, dictionary, or None) to a function or method that specifically requires a string (str) or a bytes-like object (bytes, bytearray).

This guide explains common scenarios where this error occurs, particularly with the re (regular expression) module and file I/O, and provides clear solutions.

Understanding the Error: Type Mismatch

This TypeError fundamentally means a function or method received an argument of a type it wasn't designed to handle. Many Python functions, especially those dealing with text processing (like regular expressions) or file writing in text mode, explicitly require their input to be either a string (str) or a bytes-like object. Passing other types will trigger this error.

Common Cause: Incorrect Argument Type for re Functions (re.sub, re.findall, etc.)

Functions in the re module (re.sub, re.findall, re.match, re.search, etc.) operate on strings or bytes-like objects. Passing a different type as the string to be searched or modified (often the last argument) is a primary cause of this error.

Error Example (re.sub):

import re

my_int = 100
# The third argument MUST be a string or bytes-like object
try:
result = re.sub(r'[0-9]', '_', my_int)
except TypeError as e:
print(f"Error: {e}") # Output: Error: expected string or bytes-like object

Error Example (re.findall):

import re

my_int = 100
# The second argument MUST be a string or bytes-like object
try:
result = re.findall(r'[0-9]', my_int)
except TypeError as e:
print(f"Error: {e}") # Output: Error: expected string or bytes-like object, got 'int'

The most direct solution is to explicitly convert the non-string argument to a string using the str() constructor before passing it to the re function:

import re

my_int = 100

# Convert integer to string before using re.sub()
result_sub = re.sub(r'[0-9]', '_', str(my_int))
print(result_sub) # Output: ___

# Convert integer to string before using re.findall()
result_findall = re.findall(r'[0-9]', str(my_int))
print(result_findall) # Output: ['1', '0', '0']

Handling None Values

If the variable might be None, attempting str(None) works (resulting in the string 'None'). However, if you want to treat None as an empty string for the re operation, use a conditional expression or the or shortcut:

import re

my_value = None

# Provide an empty string fallback if my_value is None (or any falsy value)
string_to_process = str(my_value or '') # Handles None, empty string, etc.
result = re.sub(r'[0-9]', '_', string_to_process)

print(repr(result)) # Output: ''

Processing List Elements

When applying re functions to items in a list that might contain non-strings, convert each item inside the loop or comprehension:

import re

my_list = [0, 'item10', 100, None, 'more_text_20']
processed_list = []

for item in my_list:
item_str = str(item or '') # Convert, handling None
result = re.sub(r'[0-9]', '_', item_str) # Process the string
processed_list.append(result)
# print(f"Original: {item}, Processed: {result}") # Optional: see intermediate steps

print(processed_list)
# Output: ['_', 'item__', '___', '', 'more_text___']

re.findall() with f.readlines() vs. f.read()

Passing the result of f.readlines() (which is a list of strings) to re.findall will cause the error. Use f.read() to get the entire file content as a single string.

import re

# Assuming 'example.txt' with multiple lines exists
try:
with open('example.txt', 'r', encoding='utf-8') as f:
# lines = f.readlines() # Incorrect: returns a list
# matches = re.findall(r'\w+', lines) # ⛔️ TypeError

file_content_str = f.read() # Correct: returns a single string
matches = re.findall(r'\w+', file_content_str) # Pass the string
print(matches) # Output: ['list', 'of', 'words', 'from', 'file']
except FileNotFoundError:
print("Error: example.txt not found.")

json.loads() vs. json.load()

When reading JSON, passing the file object itself to json.loads() (which expects a string) will cause a TypeError. Use json.load() to read directly from a file object.

import json

# Assuming 'example.json' contains: {"name": "Alice", "age": 30}
try:
with open('example.json', 'r', encoding='utf-8') as f:
# Correct: Use json.load() with the file object
my_data = json.load(f)
print(my_data) # Output: {'name': 'Alice', 'age': 30}

# Incorrect: json.loads() needs a string
# f.seek(0) # Need to go back to start if reading again
# my_data_error = json.loads(f) # ⛔️ TypeError
except FileNotFoundError:
print("Error: example.json not found.")
except json.JSONDecodeError:
print("Error: Could not decode JSON.")

Debugging: Checking Variable Types

If you're unsure why the error is occurring, always check the type of the variable being passed to the function:

my_variable = 100 # Example value causing the error

print(type(my_variable))
# Example Output: <class 'int'>

print(isinstance(my_variable, (str, bytes))) # Check if it's string or bytes
# Example Output: False
  • type() shows the exact type.
  • isinstance() checks if the object is of a specific type (or types in a tuple).

Conclusion

The TypeError: expected string or bytes-like object clearly indicates that a function requiring text or binary data received an incompatible type.

  • The most common solution, especially when working with the re module, is to explicitly convert the problematic argument to a string using str().
  • Be mindful of how you read files (read() vs. readlines()) and load JSON (load() vs. loads()) to ensure you're passing the correct data type to the functions that require strings or bytes.