KeyError: 0 in Python: Why Integer Keys Fail (2026 Fix)

You wrote data[0] expecting the first row of a DataFrame or the first item of a dict, and Python threw KeyError: 0.

The fix depends entirely on what data actually is, a pandas DataFrame, a regular dict, or a JSON-loaded dict where the integer turned into a string. This guide walks through all 4 common causes with fixes.

KeyError 0 in Python Why Integer Keys Fail (2026 Fix)

📌 Quick answer: If you’re indexing a pandas DataFrame, use df.iloc[0] instead of df[0]. If you’re indexing a regular dict, the key 0 simply doesn’t exist (check with print(list(my_dict.keys()))). If you loaded JSON, integer keys became strings: use data["0"] not data[0].

Cause 1: pandas DataFrame with non-default index

The most common cause. You expected df[0] to give you the first row, but pandas treats square-bracket access on a DataFrame as column access, not row access. df[0] means “give me the column whose label is the integer 0,” and if no such column exists, you get KeyError: 0.

import pandas as pd
df = pd.read_csv("data.csv")

df[0]              # ❌ KeyError: 0 (no column named 0)
df.iloc[0]         # ✓ first row by integer position
df.loc[0]          # ✓ first row by label (works if index is default 0,1,2...)
df.iloc[0, 0]      # ✓ first row, first column (scalar value)

Rule of thumb: use .iloc[i] for “give me the i-th row by position.” Use .loc[label] when you know the actual index label. Never use bare df[0] unless you have a column literally named with the integer 0.

Cause 2: After reset_index() with drop=False

You called df.reset_index() hoping to make row indexes go 0, 1, 2… but the old index became a new column called “index”. If you then try df.loc[0] after a filter that dropped row 0, you get KeyError.

import pandas as pd
df = pd.DataFrame({"name": ["Alice", "Bob", "Carol"]})
df_filtered = df[df["name"] != "Alice"]  # drops index 0

df_filtered.loc[0]       # ❌ KeyError: 0 (index 0 was dropped)
df_filtered.iloc[0]      # ✓ first remaining row (by position, not by label)

# Or rebuild the index so labels match positions:
df_filtered = df_filtered.reset_index(drop=True)
df_filtered.loc[0]       # ✓ works now

Rule of thumb: after any filter, sort, or join that might drop or shuffle rows, call .reset_index(drop=True) if you intend to use .loc[0] downstream. Or just use .iloc[0], which is always position-based.

Cause 3: JSON loaded integer keys as strings

JSON has no integer-keyed objects, all keys are strings. When you json.loads() a Python dict that had integer keys, they become strings during serialization, and your old data[0] code now fails.

import json

# Server sends back integer-keyed lookup as JSON
text = '{"0": "Alice", "1": "Bob"}'
data = json.loads(text)

data[0]            # ❌ KeyError: 0
data["0"]          # ✓ works (keys are strings)

# If you control the deserialization, convert keys back:
data = {int(k): v for k, v in json.loads(text).items()}
data[0]            # ✓ "Alice"

Rule of thumb: any JSON-loaded dict has string keys. If your design needs integer keys, convert in a single dict comprehension immediately after the loads() call.

Cause 4: Empty dict or filtered-to-empty result

Your code worked on the test data but breaks in production because the upstream filter returned an empty dict. data[0] raises KeyError because the dict has no keys at all.

users = filter_active_users()  # might return {}
first = users[0]               # ❌ KeyError: 0 on empty dict

# Safer pattern:
first = next(iter(users.values()), None)   # None if empty
if first is None:
    print("No active users")
else:
    print(first.name)

# Or check first:
if 0 in users:
    first = users[0]
else:
    first = None

Rule of thumb: always guard “first item” extraction with if data: or use next(iter(data.values()), default). Never assume a dict is non-empty.

Prevention: 3 Patterns to Avoid KeyError: 0

  1. Use iloc[] for pandas position lookups, never bare df[0]
  2. Convert JSON dicts at load time if you need integer keys: data = {int(k): v for k, v in json.loads(s).items()}
  3. Check len before .loc/.iloc: if len(df) > 0: first = df.iloc[0]

Frequently Asked Questions

Why does df[0] raise KeyError in pandas?

In pandas, df[col] means column access by label. df[0] tries to look up a column literally named with the integer 0, which usually doesn’t exist. For row access by position, use df.iloc[0]. For row access by index label, use df.loc[label].

Why does my JSON dict raise KeyError on integer access?

JSON only supports string keys. When you save a Python dict with integer keys to JSON and reload it, the keys become strings. data[0] fails but data[“0”] works. To recover integer keys: data = {int(k): v for k, v in json.loads(s).items()}.

What’s the difference between df.loc[0] and df.iloc[0]?

df.iloc[0] is position-based: always returns the first row regardless of index labels. df.loc[0] is label-based: returns the row with index label 0, which may not exist after filtering, sorting, or merging. Use iloc when you want “first / Nth row by position,” loc when you want “row with this specific index label.”

How do I safely get the first item from a dict or DataFrame?

For dicts: next(iter(my_dict.values()), None) returns None if the dict is empty. For DataFrames: if len(df) > 0: first = df.iloc[0] else: first = None. Both patterns prevent KeyError on empty containers.

When does reset_index() solve KeyError: 0?

After any filter, sort, or join that drops or shuffles rows in a DataFrame. df.reset_index(drop=True) rebuilds the index as 0, 1, 2… so df.loc[0] becomes equivalent to df.iloc[0] again. drop=True discards the old index; without it, the old index becomes a new “index” column.

Leave a Comment