Appendix D: Common Errors and How to Fix Them

Everyone who writes Python code encounters error messages on a regular basis—experts included. An error is not a sign of failure; it is Python explaining, as precisely as possible, what went wrong and where execution stopped.

This appendix collects the exceptions and pitfalls that researchers are most likely to encounter in empirical analysis, what each message means, and how to fix it. The examples below are live: each was executed when compiling the book, so the tracebacks match exactly what your Python environment will display.

Anatomy of a Python traceback

When Python cannot execute an instruction, it raises an exception and prints a traceback.

  1. Read from bottom to top: The very last line of the traceback displays the exception type and the specific message explaining what failed (for example, KeyError: 'age_years').
  2. Find your code: Look upward through the traceback to find the last line that points to your script file (e.g., analysis.py, line 24). That is the exact line where execution halted.
  3. Warnings vs. Exceptions: An exception terminates execution immediately. A warning (such as pandas’ SettingWithCopyWarning or FutureWarning) allows the script to continue running, but alerts you to a potential issue or deprecated syntax.

Variable and naming errors

NameError: name '...' is not defined

average = sum(sleep_hours) / len(sleep_hours)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[2], line 1
----> 1 average = sum(sleep_hours) / len(sleep_hours)

NameError: name 'sleep_hours' is not defined

What it means: Python searched your computer’s memory for an object or variable named sleep_hours and found nothing.

Common causes: - Typing mistake: Variable names in Python are strictly case-sensitive. Sleep_hours and sleep_hours are two completely different names. - Unexecuted line: In an interactive session or notebook, the cell where the variable was defined was not run, or was run in a previous session that has since been restarted. - Missing quotes around text: Writing faculty = Education instead of faculty = "Education" causes Python to look for a variable named Education.

AttributeError: '...' object has no attribute '...'

scores = [78, 85, 92, 64, 88]
scores.mean()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[3], line 2
      1 scores = [78, 85, 92, 64, 88]
----> 2 scores.mean()

AttributeError: 'list' object has no attribute 'mean'

What it means: You attempted to call a method or access a property that does not exist on that object type. Here, standard Python list objects have methods like .append() and .sort(), but no .mean() method.

How to fix it: - If working with a list, use Python’s built-in sum(scores) / len(scores) or numpy’s np.mean(scores). - If you intended to use pandas methods, convert the collection to a pandas Series: pd.Series(scores).mean(). - Check spelling: writing df.val_counts() instead of df.value_counts().

Data structure and indexing errors

KeyError: '...'

students["faculty_name"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/core/indexes/base.py:3816, in Index.get_loc(self, key)
   3815 try:
-> 3816     return self._engine.get_loc(casted_key)
   3817 except KeyError as err:

File pandas/_libs/index.pyx:172, in pandas._libs.index.IndexEngine.get_loc()
--> 172 'Could not get source, probably due dynamically evaluated source code.'

File pandas/_libs/index.pyx:201, in pandas._libs.index.IndexEngine.get_loc()
--> 201 'Could not get source, probably due dynamically evaluated source code.'

File pandas/_libs/hashtable_class_helper.pxi:7726, in pandas._libs.hashtable.PyObjectHashTable.get_item()
-> 7726 'Could not get source, probably due dynamically evaluated source code.'

File pandas/_libs/hashtable_class_helper.pxi:7734, in pandas._libs.hashtable.PyObjectHashTable.get_item()
-> 7734 'Could not get source, probably due dynamically evaluated source code.'

KeyError: 'faculty_name'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[4], line 1
----> 1 students["faculty_name"]

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/core/frame.py:4330, in DataFrame.__getitem__(self, key)
   4326 
   4327         if is_single_key:
   4328             if self.columns.nlevels > 1:
   4329                 return self._getitem_multilevel(key)
-> 4330             indexer = self.columns.get_loc(key)
   4331             if is_integer(indexer):
   4332                 indexer = [indexer]  # type: ignore[assignment]
   4333         else:

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/core/indexes/base.py:3823, in Index.get_loc(self, key)
   3818     if isinstance(casted_key, slice) or (
   3819         isinstance(casted_key, abc.Iterable)
   3820         and any(isinstance(x, slice) for x in casted_key)
   3821     ):
   3822         raise InvalidIndexError(key) from err
-> 3823     raise KeyError(key) from err
   3824 except TypeError:
   3825     # If we have a listlike key, _check_indexing_error will raise
   3826     #  InvalidIndexError. Otherwise we fall through and re-raise
   3827     #  the TypeError.
   3828     self._check_indexing_error(key)

KeyError: 'faculty_name'

What it means: In pandas or dictionaries, you requested a column name or key that does not exist.

How to fix it: - Check the exact spelling and casing of your DataFrame columns by running list(students.columns). - In the wellbeing dataset, the column is named faculty, not faculty_name. - Check for hidden whitespace in column names (e.g. 'age ' instead of 'age'), which often occurs when importing messy Excel or survey exports. Clean them with df.columns = df.columns.str.strip().

IndexError: list index out of range

faculties = ["Health Sciences", "Education", "Social Sciences"]
print(faculties[3])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[5], line 2
      1 faculties = ["Health Sciences", "Education", "Social Sciences"]
----> 2 print(faculties[3])

IndexError: list index out of range

What it means: You attempted to access an item at an index position that does not exist.

How to fix it: - Python uses 0-based indexing. In a list of three elements, the items are at positions 0, 1, and 2. Accessing index 3 asks for a fourth item. - To access the last item of any list or array safely, use negative indexing: faculties[-1].

Data type and value errors

TypeError: unsupported operand type(s)

total_participants = 600 + " students"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 total_participants = 600 + " students"

TypeError: unsupported operand type(s) for +: 'int' and 'str'

What it means: You attempted an operation on incompatible data types—here, adding a number (int) and text (str).

How to fix it: - If you want to combine numbers and text for reporting, convert the number to a string using an f-string: python f"{600} students" - If a numerical column was imported as strings (for example, containing numbers mixed with "N/A"), convert the column with pd.to_numeric(df['col'], errors='coerce').

ValueError: could not convert string to float

float("29.5 years")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[7], line 1
----> 1 float("29.5 years")

ValueError: could not convert string to float: '29.5 years'

What it means: A function expected data in a specific format, but the received value could not be parsed. float() cannot convert "29.5 years" because of the trailing text characters.

How to fix it: - Strip extraneous text characters before numeric conversion: python float("29.5 years".replace(" years", "")) - In pandas, use regular expression extraction: df['age'].str.extract(r'(\d+\.?\d*)').astype(float).

Syntax and indentation errors

SyntaxError: invalid syntax

if students['age'].mean() > 25
    print("Mature cohort")
SyntaxError: expected ':'

What it means: Python could not parse your code because the grammar rules of the language were violated.

Common causes: - Forgetting the colon : at the end of an if, for, while, or def line. - Unmatched parentheses, brackets, or quotes: opening ( without a matching ). - Using assignment = where a comparison == was required (e.g. if x = 5:).

IndentationError: expected an indented block

def calculate_bmi(weight_kg, height_m):
return weight_kg / (height_m ** 2)
IndentationError: expected an indented block after function definition

What it means: Unlike R or C++ which use curly braces {} to group code blocks, Python relies strictly on indentation (usually 4 spaces).

How to fix it: - Indent all statements inside functions, loops, and conditional blocks by 4 spaces: python def calculate_bmi(weight_kg, height_m): return weight_kg / (height_m**2) - Never mix tabs and spaces in the same file. In VS Code, ensure your indentation setting is set to “Spaces: 4”.

File and environment errors

FileNotFoundError: [Errno 2] No such file or directory

df = pd.read_csv("data/non_existent_survey.csv")
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[8], line 1
----> 1 df = pd.read_csv("data/non_existent_survey.csv")

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/io/parsers/readers.py:1866, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, skip_blank_lines, parse_dates, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, low_memory, memory_map, float_precision, storage_options, dtype_backend)
   1853 kwds_defaults = _refine_defaults_read(
   1854     dialect,
   1855     delimiter,
   (...)   1862     lineterminator=lineterminator,
   1863 )
   1864 kwds.update(kwds_defaults)
-> 1866 return _read(filepath_or_buffer, kwds)

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/io/parsers/readers.py:423, in _read(filepath_or_buffer, kwds)
    420             return result
    422 # Create the parser.
--> 423 parser = TextFileReader(filepath_or_buffer, **kwds)
    425 if chunksize or iterator:
    426     return parser

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/io/parsers/readers.py:2721, in TextFileReader.__init__(self, f, engine, **kwds)
   2718     self.options["has_index_names"] = kwds["has_index_names"]
   2720 self.handles: IOHandles | None = None
-> 2721 self._engine = self._make_engine(f, self.engine)

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/io/parsers/readers.py:2988, in TextFileReader._make_engine(self, f, engine)
   2983 is_text = engine in ("python", "python-fwf") or (
   2984     engine == "c"
   2985     and self.options.get("encoding", "utf-8") not in (None, "utf-8")
   2986 )
   2987 mode = "r" if is_text else "rb"
-> 2988 self.handles = get_handle(
   2989     f,
   2990     mode,
   2991     encoding=self.options.get("encoding", None),
   2992     compression=self.options.get("compression", None),
   2993     memory_map=self.options.get("memory_map", False),
   2994     is_text=is_text,
   2995     errors=self.options.get("encoding_errors", "strict"),
   2996     storage_options=self.options.get("storage_options", None),
   2997 )
   2998 assert self.handles is not None
   2999 f = self.handles.handle

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/site-packages/pandas/io/common.py:948, in get_handle(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)
    939         handle = open(
    940             handle,
    941             ioargs.mode,
   (...)    944             newline="",
    945         )
    946     else:
    947         # Binary mode
--> 948         handle = open(handle, ioargs.mode)
    949     handles.append(handle)
    951 # Convert BytesIO or file objects passed with an encoding

FileNotFoundError: [Errno 2] No such file or directory: 'data/non_existent_survey.csv'

What it means: Python searched the file system at the specified path and found no file with that name.

How to fix it: - Check your current working directory by running: ```python import os

print(os.getcwd()) ``- Relative file paths start from the current working directory. If your script is inscripts/and your data is indata/, writing“data/students.csv”will fail if the working directory is already insidescripts/. Use paths relative to the project root or usepathlib.Path(file).parent` for portable path resolution.

ModuleNotFoundError: No module named '...'

What it means: You ran import <package>, but Python could not find that library in the active environment.

How to fix it: - Ensure the library is installed in your terminal using pip install <package>. - In VS Code, verify that the active Python interpreter corresponds to the environment where you installed the package (Ctrl+Shift+P -> Python: Select Interpreter).

Silent data pitfalls

Some mistakes in data analysis produce no error message at all, but generate incorrect numbers. Watch out for these three pitfalls:

1. Missing value comparisons (NaN == NaN)

In scientific Python, floating-point missing values (NaN) follow the IEEE 754 standard: a missing value is never equal to anything, not even itself:

np.nan == np.nan
False

Writing students[students['age'] == np.nan] will return an empty table! Always use pandas’ .isna() or .notna():

missing_count = students["age"].isna().sum()
print(f"Students with missing age: {missing_count}")
Students with missing age: 1

2. Exponentiation (^ vs. **)

In Python, the caret symbol ^ is the bitwise XOR operator, not the mathematical power operator:

print("2 ^ 3 evaluates to:", 2 ^ 3)  # Bitwise XOR!
print("2 ** 3 evaluates to:", 2**3)  # Mathematical power (2 cubed)
2 ^ 3 evaluates to: 1
2 ** 3 evaluates to: 8

Always use ** for powers (e.g. (1 + r) ** t).

3. Modifying a slice (SettingWithCopyWarning)

When you filter a DataFrame with sub = df[df['faculty'] == 'Education'] and then try to create a new column with sub['new_col'] = 1, pandas warns you that it cannot tell whether sub is an independent copy or a view into the original DataFrame.

How to fix it: Always make an explicit copy when creating a working subset:

sub = df[df["faculty"] == "Education"].copy()
sub["new_col"] = 1