TL;DR
There are more values in the iterable than variables on the left side of the assignment. Either add variables to match, use *rest to absorb the extras, or fix what you're unpacking from.
# Broken
a, b = (1, 2, 3) # ValueError: too many values to unpack (expected 2)
# Fixed β match the count
a, b, c = (1, 2, 3)
# Fixed β collect extras with star
a, b, *rest = (1, 2, 3, 4, 5) # rest = [3, 4, 5]
Root Cause
Python unpacking is positional. The number of variables on the left must exactly match the number of values on the right. One extra value is enough to raise ValueError: too many values to unpack (expected N).
Four situations trigger this most often:
- Assigning a tuple or list to fewer variables than it contains
- Iterating a dictionary without
.items()β each key gets treated as an iterable of characters - Unpacking CSV rows or API responses that have extra fields you didn't account for
- Using
for a, b in some_listwhen each element has more than 2 items
Fix 1 β Match Variable Count
Count the values, declare the right number of variables. Obvious with literals, but easy to miss when the data comes from a function call rather than something you wrote yourself.
# Wrong
first, last = "John", "Paul", "George", "Ringo"
# Right
first, second, third, fourth = "John", "Paul", "George", "Ringo"
Good for fixed structures where you always know the exact count ahead of time.
Fix 2 β Use the Star (*) Catch-All Syntax
Only need specific positions? Let * absorb the rest into a list:
# Grab first and last, collect middle
first, *middle, last = [10, 20, 30, 40, 50]
print(first) # 10
print(last) # 50
print(middle) # [20, 30, 40]
# Grab first two, throw away the rest
a, b, *_ = get_csv_row() # _ is the discard convention
Star syntax handles variable-length data cleanly β CSV rows, API payloads, anything where extra fields may appear without warning. The *_ pattern is idiomatic Python for "I know there's more, I don't need it."
Fix 3 β Dictionary Iteration Without .items()
This one catches a lot of people. Iterating a dict without .items() gives you keys, not (key, value) pairs. If a key is a string like "name", Python sees 4 individual characters β unpacking into two variables fails immediately:
# Broken β iterates keys, not (key, value) pairs
data = {"name": "Alice", "age": 30}
for key, value in data: # ValueError: "name" has 4 chars, not 2
print(key, value)
# Fixed
for key, value in data.items():
print(key, value)
# name Alice
# age 30
Use .items() for key-value pairs, .keys() for keys alone, .values() for values alone.
Fix 4 β Unpacking Inside a Loop
Each element in your list has 3 fields, but you're only unpacking 2. Python can't guess which field to drop:
# Data has 3 fields per row
records = [
("alice", "alice@example.com", "admin"),
("bob", "bob@example.com", "user"),
]
# Broken β only 2 variables for 3 values
for name, email in records: # ValueError
print(name, email)
# Fixed β add the third variable
for name, email, role in records:
print(name, email, role)
# Fixed β use star if role isn't needed
for name, email, *_ in records:
print(name, email)
Fix 5 β Unpacking Function Return Values
Return values drift. A function that once returned 2 values gets a third field added later β a timestamp, a status code, a refresh rate. Every caller unpacking into exactly 2 variables breaks silently until runtime:
def get_dimensions():
return 1920, 1080, 144 # refresh rate added later
# Broken
width, height = get_dimensions() # ValueError
# Fixed
width, height, refresh = get_dimensions()
# If you genuinely don't need the extra value
width, height, *_ = get_dimensions()
If you own the function, treat adding a return value as a breaking change. Document it. Callers need to know.
Fix 6 β CSV / File Parsing
CSV files are particularly prone to this. Trailing commas, optional fields, schema changes β all produce rows with more columns than you expected:
import csv
# Broken if any row has more than 2 columns
with open("data.csv") as f:
reader = csv.reader(f)
for row in reader:
name, score = row # ValueError on extra-column rows
# Fixed β index into the row instead
with open("data.csv") as f:
reader = csv.reader(f)
for row in reader:
name = row[0]
score = row[1]
# Better β use DictReader for named columns
with open("data.csv") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["score"])
How to Confirm the Fix Worked
No ValueError on the next run β that's the confirmation. For external data sources like APIs, files, or databases, print a raw sample before writing the unpacking logic:
# Check the shape before committing to unpacking
data = some_function()
print(type(data), len(data), data)
# Then unpack with confidence
a, b, c = data
One print statement upfront saves ten minutes of debugging later.
Quick Diagnostic Checklist
- Count values vs. variables β they must match, or use
*rest - Iterating a dict? Add
.items()to get key-value pairs - Loop unpacking
for a, b in list? Check each element's actual length first - Parsing CSV or JSON? Columns may have changed β print a raw row before unpacking
- Calling a function that returns a tuple? Verify the return value count hasn't grown

