Skip to content

Fix: Python ValueError: Too Many Values to Unpack

FixDevs · (Updated: )

Part of:  Python Errors

Quick Answer

Learn why Python raises ValueError too many values to unpack and how to fix it when unpacking tuples, iterating dictionaries, parsing files, and using zip or enumerate.

The Crash That Means “Your Data Is Not What You Think”

Personally, this is one of the errors I am most glad Python raises. JavaScript would silently assign undefined to the extra variables and let the bug travel three function calls deep; Python crashes on the spot with a precise count of what it expected versus what it got. I have caught more upstream data bugs from this one error than any other. You run a Python script and get:

Traceback (most recent call last):
  File "app.py", line 3, in <module>
    a, b = (1, 2, 3)
ValueError: too many values to unpack (expected 2)

Or the reverse:

ValueError: not enough values to unpack (expected 3, got 2)

Both errors come from the same root cause: a mismatch between the number of variables on the left side of an assignment and the number of values on the right side.

This error shows up in tuple unpacking, dictionary iteration, function return values, file parsing, and several other common patterns. Each scenario has a different fix.

How Unpacking Actually Works

Python supports unpacking assignment, where you assign multiple variables at once from an iterable:

a, b, c = [1, 2, 3]

Python expects the number of variables on the left to match the number of values on the right exactly. When they do not match, you get a ValueError.

The “too many values” variant means the right side has more elements than the left side has variables. The “not enough values” variant means the opposite.

This is strict by design. Python does not silently drop extra values or fill missing ones with None. It forces you to be explicit about how many values you expect, which prevents subtle bugs where data silently goes missing.

The error appears in several common scenarios:

  • Unpacking a tuple or list with the wrong number of variables
  • Iterating over a dictionary without calling .items()
  • Ignoring extra return values from a function
  • Parsing CSV rows or file lines with unexpected columns
  • Misusing enumerate() or zip()
  • Nested unpacking with incorrect structure

Each of these has a specific fix, covered below.

Diagnostic Timeline

The reflex is to count the variables on the left side and adjust. That fixes the immediate traceback but hides the real bug: the data shape changed and your code never noticed. Spend two minutes inspecting the producer, not the consumer, before you touch the unpacking line.

Minute 0 — Print the actual value, not the type. The traceback tells you expected 2, got 3 but not what the three values are. Insert one line above the failing assignment:

print(repr(value), type(value), len(value) if hasattr(value, "__len__") else "no len")
a, b = value

repr matters because it shows quoting and trailing whitespace; str would hide them. If value is a string 'Hi!' you will see 'Hi!' with three characters, instantly explaining the “string iterated into characters” trap from Fix 3.

Minute 1 — Identify the producer. Walk back one call. Is the right-hand side a function return, a CSV row, a database fetch, an API response, a generator? Each has a different failure mode:

  • Function return: signature changed in a recent commit. Check git blame on the function definition.
  • CSV row: an upstream system added or removed a column. Run awk -F, '{print NF}' data.csv | sort -u to see how many column counts exist.
  • Database fetch: SELECT * returns whatever the schema currently has. A migration added a column overnight.
  • API response: provider added a field. Check the response with curl | jq 'length'.
  • Generator: it was already partially consumed by an earlier next() or another for loop sharing the same iterator.

Minute 2 — Confirm whether the bug is shape or order. “Too many” and “not enough” both mean shape mismatch. But a hidden third category is wrong order: name, age = get_user() succeeds when the function returns (name, age) and silently produces garbage when it returns (age, name). If the unpacking works but downstream code misbehaves, log every unpacked variable with its name:

name, age = get_user()
print(f"unpacked name={name!r} age={age!r}")

Minute 3 — Probe whether the iterable is exhausted. Generators raise the “not enough values” variant after they have already been iterated:

gen = (x for x in [1, 2, 3])
a = next(gen)            # 1
b, c, d = gen            # ValueError: not enough values to unpack (expected 3, got 2)

If repr(value) shows <generator object ...>, force-materialize it with list(value) first.

Minute 4 — Decide between adapting code or rejecting data. If the producer is your code, fix the contract. If the producer is external (CSV, API, user input), wrap unpacking in a try/except ValueError and log the bad row — see Fix 5 and the Still Not Working section below.

Fix 1: Match the Number of Variables to the Iterable

The simplest case. You have a tuple or list with N elements but only M variables on the left.

Broken:

coordinates = (10, 20, 30)
x, y = coordinates  # ValueError: too many values to unpack (expected 2)

Fixed:

coordinates = (10, 20, 30)
x, y, z = coordinates

If you genuinely do not know how many elements the iterable contains, check its length first:

coordinates = (10, 20, 30)
if len(coordinates) == 2:
    x, y = coordinates
elif len(coordinates) == 3:
    x, y, z = coordinates

Or index directly instead of unpacking:

x = coordinates[0]
y = coordinates[1]

My personal debugging instinct on this error is to print the value being unpacked one line above the crash. Nine times out of ten the bug is in the data, not the code: an API returned a record with an extra field, a CSV had a trailing comma, a dict you thought had two keys actually has three. A quick print(len(coordinates), coordinates) shows you the real shape and the unpacking line writes itself.

Fix 2: Use Star Expressions to Capture Extra Values

Python 3 introduced star expressions (*variable) for unpacking. This lets you capture the “rest” of an iterable into a list without knowing its exact length.

Capture trailing values:

first, *rest = [1, 2, 3, 4, 5]
# first = 1
# rest = [2, 3, 4, 5]

Capture leading values:

*rest, last = [1, 2, 3, 4, 5]
# rest = [1, 2, 3, 4]
# last = 5

Capture the middle:

first, *middle, last = [1, 2, 3, 4, 5]
# first = 1
# middle = [2, 3, 4]
# last = 5

Discard extra values with *_:

If you only need certain values and want to throw away the rest:

name, age, *_ = ("Alice", 30, "Engineer", "NYC", "USA")
# name = "Alice"
# age = 30
# _ = ["Engineer", "NYC", "USA"] (discarded)

Star expressions are useful when dealing with data of variable length, such as log lines, CSV rows with optional columns, or API responses that may include extra fields.

You can only use one star expression per unpacking assignment. This raises a SyntaxError:

*a, *b = [1, 2, 3]  # SyntaxError: multiple starred expressions in assignment

Fix 3: Use .items() When Iterating Dictionaries

A common mistake is trying to unpack both keys and values from a dictionary using a plain for loop.

Broken:

user = {"name": "Alice", "age": 30}
for key, value in user:
    print(key, value)
# ValueError: too many values to unpack (expected 2)

Wait, this actually raises ValueError: too many values to unpack only in specific cases. Iterating over a plain dictionary yields keys only (strings). If a key has more than two characters, unpacking it into two variables fails because the string has too many characters.

for key, value in user:
    # "name" is iterated as 'n', 'a', 'm', 'e' — 4 values, expected 2
    pass

Fixed — use .items():

user = {"name": "Alice", "age": 30}
for key, value in user.items():
    print(key, value)

The .items() method returns (key, value) tuples, which unpack cleanly into two variables.

Similarly, if you only need values, use .values():

for value in user.values():
    print(value)

This is one of the most common sources of this error for Python beginners. If you see this error inside a for loop with a dictionary, check whether you forgot .items().

For related dictionary errors, see Python KeyError, which covers the partner mistake of indexing a key that does not exist.

Fix 4: Handle Function Return Values Correctly

Functions that return tuples are another frequent source of unpacking errors. The caller must unpack the correct number of values.

Broken:

def get_user_info():
    return "Alice", 30, "Engineer"

name, age = get_user_info()
# ValueError: too many values to unpack (expected 2)

Fixed — unpack all three:

name, age, role = get_user_info()

Fixed — use star expression to ignore extras:

name, age, *_ = get_user_info()

Fixed — assign to a single variable and index:

info = get_user_info()
name = info[0]
age = info[1]

This error commonly appears when a function’s return signature changes during development. You add a third return value to the function but forget to update every call site. Your IDE or linter can catch this if you use type hints:

from typing import Tuple

def get_user_info() -> Tuple[str, int, str]:
    return "Alice", 30, "Engineer"

With type hints, tools like mypy flag mismatched unpacking at static analysis time, before you ever run the code.

Fix 5: Fix CSV and File Parsing

When reading CSV files or splitting lines from a file, the number of columns can vary row by row. A single row with an extra comma or an unexpected delimiter causes this error.

Broken:

with open("data.csv") as f:
    for line in f:
        name, age, city = line.strip().split(",")
        # ValueError if any row has more or fewer than 3 columns

If a line contains Alice,30,NYC,USA, the split produces four values but you only have three variables.

Fixed — use star expression for extra columns:

with open("data.csv") as f:
    for line in f:
        name, age, city, *extra = line.strip().split(",")

Fixed — limit the split:

with open("data.csv") as f:
    for line in f:
        name, age, city = line.strip().split(",", maxsplit=2)

The maxsplit=2 argument tells split() to only split on the first two commas. Everything after the second comma becomes part of city. This is useful when the last field might contain the delimiter character (for example, addresses with commas).

Fixed — use the csv module:

For real-world CSV parsing, use Python’s built-in csv module instead of manual splitting. It handles quoting, escaping, and edge cases properly:

import csv

with open("data.csv") as f:
    reader = csv.reader(f)
    for row in reader:
        if len(row) >= 3:
            name, age, city = row[0], row[1], row[2]

A bug I have shipped to production twice (and refused to ship a third time): treating CSVs as comma-separated text and reaching for .split(","). Real CSVs have quoted fields with embedded commas like "New York, NY", and the moment your data contains one, your manual splitter blows the unpacking. I now reach for csv.reader reflexively even for “tiny throwaway scripts” — they always survive longer than I expected.

Fixed — validate row length:

with open("data.csv") as f:
    for line_num, line in enumerate(f, 1):
        parts = line.strip().split(",")
        if len(parts) != 3:
            print(f"Skipping malformed row {line_num}: {parts}")
            continue
        name, age, city = parts

This approach logs bad rows instead of crashing, which is essential for data pipelines where input quality varies.

For related file-handling issues, see Python FileNotFoundError — the failure mode you hit when the CSV path itself is wrong.

Fix 6: Fix enumerate() Unpacking

enumerate() returns (index, element) tuples. A common mistake is forgetting the index or using the wrong number of variables.

Broken — forgetting that enumerate returns pairs:

names = ["Alice", "Bob", "Charlie"]
for name in enumerate(names):
    print(name)
    # name is (0, 'Alice'), not 'Alice'

This does not raise an error itself, but if you later try to use name as a string, you get a TypeError. The real ValueError shows up when you try to unpack three values:

for index, first, last in enumerate(["Alice Smith", "Bob Jones"]):
    print(index, first, last)
# ValueError: not enough values to unpack (expected 3, got 2)

Here, enumerate() produces (0, "Alice Smith") — two values, not three.

Fixed — unpack correctly, then split the string:

for index, full_name in enumerate(["Alice Smith", "Bob Jones"]):
    first, last = full_name.split()
    print(index, first, last)

Fixed — enumerate over pre-split data:

names = [("Alice", "Smith"), ("Bob", "Jones")]
for index, (first, last) in enumerate(names):
    print(index, first, last)

Notice the parentheses around (first, last) in the second example. This is nested unpacking — you unpack the enumerate tuple into index and a sub-tuple, then unpack the sub-tuple into first and last. Without those parentheses, Python tries to unpack three values from the enumerate tuple, which only has two.

Fix 7: Fix zip() Misuse

zip() combines multiple iterables element-wise into tuples. Unpacking errors arise when you expect the wrong number of elements or when the inner iterables are themselves tuples.

Broken — wrong number of variables:

names = ["Alice", "Bob"]
ages = [30, 25]
cities = ["NYC", "LA"]

for name, age in zip(names, ages, cities):
    print(name, age)
# ValueError: too many values to unpack (expected 2)

Each tuple from zip(names, ages, cities) contains three elements.

Fixed:

for name, age, city in zip(names, ages, cities):
    print(name, age, city)

Broken — zipping a list of tuples with another list:

pairs = [(1, 2), (3, 4)]
labels = ["a", "b"]

for (x, y), label in zip(pairs, labels):
    # This works fine
    pass

for x, y, label in zip(pairs, labels):
    # ValueError — zip produces ((1, 2), "a"), not (1, 2, "a")
    pass

Fixed — use nested unpacking:

for (x, y), label in zip(pairs, labels):
    print(x, y, label)

When working with zip(), remember that it produces tuples with one element per input iterable. If one of those elements is itself a tuple, you need nested parentheses to unpack it.

Using zip_longest for uneven iterables:

By default, zip() stops at the shortest iterable. If your iterables have different lengths, use itertools.zip_longest:

from itertools import zip_longest

names = ["Alice", "Bob", "Charlie"]
ages = [30, 25]

for name, age in zip_longest(names, ages, fillvalue=None):
    print(name, age)
# Alice 30
# Bob 25
# Charlie None

This avoids silent data loss from mismatched lengths.

Fix 8: Fix Nested Unpacking

Nested unpacking lets you unpack complex structures in a single assignment. The syntax mirrors the structure of the data.

Broken:

data = [("Alice", (90, 85, 92)), ("Bob", (78, 88, 95))]

for name, score1, score2, score3 in data:
    print(name, score1, score2, score3)
# ValueError: not enough values to unpack (expected 4, got 2)

Each element in data is a 2-tuple: ("Alice", (90, 85, 92)). Python sees two values, but you asked for four.

Fixed — nest the parentheses:

for name, (score1, score2, score3) in data:
    print(name, score1, score2, score3)

The parentheses around (score1, score2, score3) tell Python to unpack the inner tuple separately.

More complex nesting:

records = [
    ("Alice", ("Engineering", ("Python", "Go")), 95000),
    ("Bob", ("Marketing", ("SEO", "PPC")), 72000),
]

for name, (dept, (skill1, skill2)), salary in records:
    print(f"{name} in {dept}: {skill1}, {skill2} — ${salary}")

Nested unpacking is powerful but gets hard to read past two levels. For deeply nested structures, consider unpacking in steps:

for record in records:
    name = record[0]
    dept = record[1][0]
    skills = record[1][1]
    salary = record[2]

Or use named tuples or dataclasses to give your data meaningful structure:

from dataclasses import dataclass
from typing import List

@dataclass
class Employee:
    name: str
    department: str
    skills: List[str]
    salary: int

This eliminates unpacking errors entirely because you access fields by name instead of position.

For related type-related issues, see Python TypeError: ‘NoneType’ object is not subscriptable, which is the next error you hit if your producer returns None instead of a tuple.

Unpacking Failures I Have Seen in the Wild

If none of the fixes above solve your case, these are the less obvious scenarios I have personally tracked down.

The Data Changes at Runtime

Your unpacking code might work fine in testing but fail in production because the data shape varies. Add defensive checks:

def safe_unpack(iterable, expected):
    items = list(iterable)
    if len(items) != expected:
        raise ValueError(
            f"Expected {expected} values, got {len(items)}: {items}"
        )
    return items

This gives you a clear error message showing the actual data that caused the mismatch, making debugging much faster.

Unpacking a Generator

Generators do not have a len(), so you cannot check their length before unpacking. Convert to a list first:

def generate_values():
    yield 1
    yield 2
    yield 3

# Risky — you don't know how many values the generator yields
a, b = generate_values()  # ValueError

# Safe — convert first, then check
values = list(generate_values())
if len(values) == 2:
    a, b = values

Unpacking Inside a Comprehension

List comprehensions with unpacking follow the same rules:

# Broken
data = [(1, 2, 3), (4, 5, 6)]
result = [a + b for a, b in data]  # ValueError

# Fixed
result = [a + b for a, b, _ in data]
# Or
result = [row[0] + row[1] for row in data]

Unpacking a String

Strings are iterable in Python, so you can unpack them into individual characters. This frequently causes surprises:

a, b = "Hi"       # Works: a='H', b='i'
a, b = "Hello"    # ValueError: too many values to unpack (expected 2)

If you see this error with a string, you are probably unpacking a variable you expected to be a tuple or list, but it is actually a string. Check the type of the value:

data = get_data()
print(type(data), data)  # Is it actually what you expect?
a, b = data

Unpacking Across Python 2 and 3

Star expressions (*rest) only work in Python 3. If you are maintaining legacy Python 2 code, you cannot use them. Use slicing instead:

# Python 2 compatible
values = (1, 2, 3, 4, 5)
first = values[0]
rest = values[1:]

A Single-Element Tuple Bites You

A common confusion: (value) is just value in parentheses, not a one-tuple. The actual one-tuple needs a trailing comma — (value,). If a function returns return (result,) and you write result = function(), you assign the tuple, not the value. Try to use result later and you get either silent wrong behavior or a ValueError on the next unpack. Always inspect with repr() if you suspect a stray comma in the producer.

Walrus Operator Caching the Old Length

If you use the walrus operator to cache an iterable and the data is mutated elsewhere mid-loop, you can unpack from a stale snapshot:

while (chunk := producer.next()) and len(chunk) == 3:
    a, b, c = chunk  # ValueError once producer returns 2-tuples

The check happens once at the start of the iteration, but producer.next() may yield differently shaped chunks. Re-check len(chunk) inside the loop or normalize at the producer.

Streaming JSON Arrays from an HTTP Response

When you stream-parse JSON line-delimited responses, partial lines arrive at the buffer boundary. Splitting the buffer on newlines and unpacking each line as [key, value] = line.split(":") fails on the first truncated line. Buffer until you hit a newline before parsing, or use ijson for true streaming.

Using a Try/Except Block

When unpacking data from external sources (APIs, files, user input), wrap the unpacking in a try/except to handle malformed data gracefully:

for row in data:
    try:
        name, age, city = row
    except ValueError:
        print(f"Skipping malformed row: {row}")
        continue

This prevents your program from crashing on a single bad record and lets you log or collect the problematic entries for later review.

For more Python debugging strategies, see Python IndexError: list index out of range, which is the index-based equivalent of this unpacking error.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles