Recently, I was working on a data analysis project involving customer sales data from different US states. I needed to create nested dictionaries to organize sales information by state, city, and product categories.
The challenge I faced was the dreaded KeyError that kept popping up whenever I tried to generate nested dictionary structures. This error occurs when you try to access or assign values to keys that don’t exist yet in your dictionary.
In this comprehensive guide, I’ll share five proven methods I’ve developed in my Python experience to handle KeyError when generating nested dictionaries. Each method comes with real-world examples and complete code implementations.
What Causes KeyError in Nested Dictionary Generation?
Before diving into solutions, let me explain what triggers this error. When you try to create nested structures like sales_data['California']['Los Angeles']['Electronics'] = 1500, Python throws a KeyError if any intermediate keys don’t exist.
This happens because Python can’t create nested levels automatically. You need to ensure each level exists before accessing the next one.
Method 1 – Use Try-Except Blocks
The try-except approach is the simple method I use when dealing with uncertain key existence. It allows you to handle the KeyError gracefully and create the necessary structure on the fly.
Here’s how I implement this method:
# Sample US sales data
sales_data = {}
transactions = [
('California', 'Los Angeles', 'Electronics', 1500),
('California', 'San Francisco', 'Clothing', 800),
('Texas', 'Houston', 'Electronics', 1200),
('Texas', 'Austin', 'Books', 300),
('California', 'Los Angeles', 'Books', 400),
('New York', 'New York City', 'Electronics', 2000),
('New York', 'Buffalo', 'Clothing', 600)
]
def create_nested_dict_try_except(data, transactions):
for state, city, category, amount in transactions:
try:
# Try to access existing nested structure
data[state][city][category] += amount
except KeyError:
# Handle missing keys by creating the structure
if state not in data:
data[state] = {}
if city not in data[state]:
data[state][city] = {}
if category not in data[state][city]:
data[state][city][category] = 0
data[state][city][category] += amount
return data
# Generate nested dictionary
result = create_nested_dict_try_except(sales_data, transactions)
# Display results
for state, cities in result.items():
print(f"\n{state}:")
for city, categories in cities.items():
print(f" {city}:")
for category, amount in categories.items():
print(f" {category}: ${amount}")I executed the above example code and added the screenshot below.

The advantage of this method is that it explicitly handles the error case. However, it can become verbose when dealing with deeply nested structures.
Method 2 – Use the get() Method with Default Values
Python’s get() method is my go-to solution for cleaner code. It returns a default value if the key doesn’t exist, preventing KeyError from occurring in the first place.
def create_nested_dict_get_method(transactions):
sales_data = {}
for state, city, category, amount in transactions:
# Use get() method to safely access nested levels
state_dict = sales_data.get(state, {})
city_dict = state_dict.get(city, {})
# Update the amount, defaulting to 0 if category doesn't exist
city_dict[category] = city_dict.get(category, 0) + amount
# Rebuild the nested structure
state_dict[city] = city_dict
sales_data[state] = state_dict
return sales_data
# Sample data - US restaurant sales by region
restaurant_transactions = [
('West Coast', 'Seattle', 'Fast Food', 3500),
('West Coast', 'Portland', 'Fine Dining', 5000),
('East Coast', 'Boston', 'Fast Food', 2800),
('East Coast', 'Boston', 'Casual Dining', 4200),
('West Coast', 'Seattle', 'Casual Dining', 3800),
('South', 'Atlanta', 'Fast Food', 3200),
('South', 'Miami', 'Fine Dining', 6500)
]
restaurant_sales = create_nested_dict_get_method(restaurant_transactions)
print("Restaurant Sales by Region:")
for region, cities in restaurant_sales.items():
print(f"\n{region}:")
for city, categories in cities.items():
print(f" {city}:")
for category, amount in categories.items():
print(f" {category}: ${amount:,}")I executed the above example code and added the screenshot below.

This method provides cleaner, more readable code compared to try-except blocks.
Method 3 – Use defaultdict from Collections
The defaultdict is my preferred method for handling nested dictionaries. It automatically creates missing keys with default values, eliminating KeyError.
from collections import defaultdict
def create_nested_defaultdict():
# Create a nested defaultdict structure
# This automatically handles missing keys
return defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
def populate_nested_defaultdict(data, transactions):
for state, city, category, amount in transactions:
# No KeyError possible - defaultdict handles missing keys
data[state][city][category] += amount
return data
# Convert defaultdict to regular dict for better display
def defaultdict_to_dict(d):
if isinstance(d, defaultdict):
d = {k: defaultdict_to_dict(v) for k, v in d.items()}
return d
# Sample US university enrollment data
enrollment_data = [
('California', 'Berkeley', 'Engineering', 5000),
('California', 'Los Angeles', 'Business', 3000),
('Massachusetts', 'Cambridge', 'Engineering', 4500),
('Texas', 'Austin', 'Liberal Arts', 6000),
('California', 'Berkeley', 'Business', 2500),
('New York', 'Ithaca', 'Engineering', 3500),
('Massachusetts', 'Cambridge', 'Business', 2800)
]
# Create and populate nested dictionary
university_enrollment = create_nested_defaultdict()
result = populate_nested_defaultdict(university_enrollment, enrollment_data)
# Convert to regular dict for display
final_result = defaultdict_to_dict(result)
print("University Enrollment by State and City:")
for state, cities in final_result.items():
print(f"\n{state}:")
for city, programs in cities.items():
print(f" {city}:")
for program, count in programs.items():
print(f" {program}: {count:,} students")I executed the above example code and added the screenshot below.

The defaultdict approach is incredibly powerful because it handles all the nested key creation automatically.
Method 4 – Use setdefault() Method
The setdefault() method in Python is another elegant solution I frequently use. It returns the value if the key exists, or sets and returns a default value if it doesn’t.
def create_nested_dict_setdefault(transactions):
sales_data = {}
for state, city, category, amount in transactions:
# Use setdefault to ensure each level exists
state_dict = sales_data.setdefault(state, {})
city_dict = state_dict.setdefault(city, {})
# Update the amount using setdefault for the final level
city_dict[category] = city_dict.setdefault(category, 0) + amount
return sales_data
# Sample US retail store data
retail_transactions = [
('Florida', 'Miami', 'Groceries', 15000),
('Florida', 'Orlando', 'Electronics', 8000),
('Illinois', 'Chicago', 'Groceries', 12000),
('Illinois', 'Springfield', 'Clothing', 4500),
('Florida', 'Miami', 'Electronics', 9500),
('Colorado', 'Denver', 'Sporting Goods', 6000),
('Colorado', 'Boulder', 'Books', 2500)
]
retail_sales = create_nested_dict_setdefault(retail_transactions)
print("Retail Sales by State and City:")
total_sales = 0
for state, cities in retail_sales.items():
state_total = 0
print(f"\n{state}:")
for city, categories in cities.items():
city_total = 0
print(f" {city}:")
for category, amount in categories.items():
print(f" {category}: ${amount:,}")
city_total += amount
print(f" City Total: ${city_total:,}")
state_total += city_total
print(f" State Total: ${state_total:,}")
total_sales += state_total
print(f"\nGrand Total: ${total_sales:,}")I executed the above example code and added the screenshot below.

The setdefault() method provides a balance between readability and functionality.
Method 5 – Use a Custom Class with missing Method
For complex applications, I sometimes create a custom dictionary class that handles missing keys automatically. This approach gives you the most control over the behavior.
class AutoNestedDict(dict):
def __missing__(self, key):
# Automatically create a new AutoNestedDict for missing keys
value = self[key] = type(self)()
return value
def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def create_custom_nested_dict(transactions):
data = AutoNestedDict()
for state, city, category, amount in transactions:
# No KeyError possible - custom class handles everything
if category in data[state][city]:
data[state][city][category] += amount
else:
data[state][city][category] = amount
return data
# Convert custom dict to regular dict for JSON serialization
def auto_dict_to_dict(d):
result = {}
for key, value in d.items():
if isinstance(value, AutoNestedDict):
result[key] = auto_dict_to_dict(value)
else:
result[key] = value
return result
# Sample US healthcare spending data
healthcare_spending = [
('California', 'San Diego', 'Hospitals', 500000),
('California', 'Sacramento', 'Clinics', 200000),
('Texas', 'Dallas', 'Hospitals', 450000),
('Texas', 'Houston', 'Emergency Care', 300000),
('California', 'San Diego', 'Emergency Care', 180000),
('New York', 'Albany', 'Hospitals', 400000),
('New York', 'Rochester', 'Clinics', 150000)
]
healthcare_data = create_custom_nested_dict(healthcare_spending)
final_healthcare_data = auto_dict_to_dict(healthcare_data)
print("Healthcare Spending by State and City:")
for state, cities in final_healthcare_data.items():
print(f"\n{state}:")
for city, categories in cities.items():
print(f" {city}:")
for category, amount in categories.items():
print(f" {category}: ${amount:,}")This custom class approach is perfect when you need consistent behavior across your entire application.
Real-World Application Example
Let me share a comprehensive example from a recent project where I analyzed US e-commerce data. This demonstrates how to apply these concepts in a production environment:
from collections import defaultdict
import json
from datetime import datetime
class ECommerceAnalyzer:
def __init__(self):
self.sales_data = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))
self.customer_data = defaultdict(lambda: defaultdict(int))
def process_transactions(self, transactions):
"""Process e-commerce transactions and build nested analytics"""
for transaction in transactions:
state = transaction['state']
product_category = transaction['category']
month = transaction['month']
amount = transaction['amount']
customer_id = transaction['customer_id']
# Build sales analytics
self.sales_data[state][product_category][month] += amount
# Build customer analytics
self.customer_data[state][month] += 1
def get_top_performing_states(self, limit=5):
"""Get top performing states by total sales"""
state_totals = {}
for state, categories in self.sales_data.items():
total = sum(
sum(months.values()) for months in categories.values()
)
state_totals[state] = total
return sorted(state_totals.items(), key=lambda x: x[1], reverse=True)[:limit]
def get_seasonal_trends(self, state):
"""Analyze seasonal trends for a specific state"""
if state not in self.sales_data:
return {}
monthly_totals = defaultdict(float)
for category, months in self.sales_data[state].items():
for month, amount in months.items():
monthly_totals[month] += amount
return dict(monthly_totals)
def export_report(self, filename):
"""Export comprehensive report to JSON"""
report = {
'generated_at': datetime.now().isoformat(),
'top_states': self.get_top_performing_states(),
'detailed_data': {}
}
# Convert defaultdict to regular dict for JSON serialization
for state in self.sales_data:
report['detailed_data'][state] = {
'sales_by_category': dict(self.sales_data[state]),
'seasonal_trends': self.get_seasonal_trends(state),
'total_customers': sum(self.customer_data[state].values())
}
with open(filename, 'w') as f:
json.dump(report, f, indent=2, default=str)
# Sample e-commerce data
sample_transactions = [
{'state': 'California', 'category': 'Electronics', 'month': 'January', 'amount': 15000, 'customer_id': 'C001'},
{'state': 'California', 'category': 'Clothing', 'month': 'January', 'amount': 8000, 'customer_id': 'C002'},
{'state': 'Texas', 'category': 'Electronics', 'month': 'January', 'amount': 12000, 'customer_id': 'C003'},
{'state': 'California', 'category': 'Electronics', 'month': 'February', 'amount': 18000, 'customer_id': 'C001'},
{'state': 'Florida', 'category': 'Home & Garden', 'month': 'January', 'amount': 9000, 'customer_id': 'C004'},
{'state': 'New York', 'category': 'Books', 'month': 'February', 'amount': 5000, 'customer_id': 'C005'},
{'state': 'California', 'category': 'Electronics', 'month': 'March', 'amount': 22000, 'customer_id': 'C006'},
{'state': 'Texas', 'category': 'Clothing', 'month': 'February', 'amount': 11000, 'customer_id': 'C007'},
]
# Run analysis
analyzer = ECommerceAnalyzer()
analyzer.process_transactions(sample_transactions)
print("E-Commerce Sales Analysis:")
print("\nTop Performing States:")
for i, (state, total) in enumerate(analyzer.get_top_performing_states(), 1):
print(f"{i}. {state}: ${total:,.2f}")
print(f"\nCalifornia Seasonal Trends:")
ca_trends = analyzer.get_seasonal_trends('California')
for month, amount in sorted(ca_trends.items()):
print(f" {month}: ${amount:,.2f}")
# Export detailed report
analyzer.export_report('ecommerce_analysis.json')
print(f"\nDetailed report exported to 'ecommerce_analysis.json'")After working with Python nested dictionaries for over a decade, I can confidently say that handling KeyError doesn’t have to be a frustrating experience. The five methods I’ve shared in this guide each serve different purposes and scenarios.
For most of my projects, I reach for defaultdict when I need automatic key creation and the get() method for simpler structures. The try-except approach works best when I need custom error handling logic, while setdefault() provides the perfect balance for moderate complexity.
You may also read other dictionary tutorials:
- Check If Two Dictionaries Are Equal in Python
- Python Copy Dict Without One Key
- Convert a Dict to String in Python
- Python Nested Dictionary to JSON

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.