pandas pivot_table KeyError (causes and fixes)

A KeyError from pandas pivot_table means either a name you passed to index=, columns= or values= isn't a column (case, trailing spaces), or you're reading the result wrong: list aggfunc gives MultiIndex columns, and the index= column becomes the index. Tested on pandas 2.3.3 and 3.1.0.

1. KeyError from the pivot_table call: a name that isn't a column

import pandas as pd
df = pd.DataFrame({"region": ["N", "N", "S"], "Product": ["x", "y", "x"],
                   "sales": [10, 20, 5]})
for kw in [dict(index="region", columns="product", values="sales"),
           dict(index="region ", columns="Product", values="sales"),
           dict(index="region", columns="Product", values=["sales", "cost"])]:
    try:
        df.pivot_table(aggfunc="sum", **kw)
        print(kw, "ok")
    except KeyError as e:
        print(kw, "-> KeyError:", e)

Output, identical on pandas 2.3.3 and 3.1.0:

{'index': 'region', 'columns': 'product', 'values': 'sales'} -> KeyError: 'product'
{'index': 'region ', 'columns': 'Product', 'values': 'sales'} -> KeyError: 'region '
{'index': 'region', 'columns': 'Product', 'values': ['sales', 'cost']} -> KeyError: 'cost'

The KeyError names the exact argument value pandas couldn't find: a case mismatch (product vs Product), a stray space ('region '), or one missing entry in a values list. Print df.columns.tolist() and compare character by character; headers read from CSV or Excel often carry spaces (see KeyError on a column that exists).

2. KeyError when reading the result: MultiIndex columns and the index

import pandas as pd
df = pd.DataFrame({"region": ["N", "N", "S"], "Product": ["x", "y", "x"],
                   "sales": [10, 20, 5]})
pt = df.pivot_table(index="region", columns="Product", values="sales",
                    aggfunc=["sum"])
print(pt.columns.tolist())
try:
    pt["x"]
except KeyError as e:
    print("pt['x'] -> KeyError:", e)
print(pt[("sum", "x")].tolist())
flat = df.pivot_table(index="region", columns="Product", values="sales", aggfunc="sum")
print(flat.columns.tolist(), flat["x"].tolist())
try:
    flat["region"]
except KeyError as e:
    print("flat['region'] -> KeyError:", e)
print(flat.reset_index().columns.tolist())

Output, identical on pandas 2.3.3 and 3.1.0:

[('sum', 'x'), ('sum', 'y')]
pt['x'] -> KeyError: 'x'
[10.0, 5.0]
['x', 'y'] [10.0, 5.0]
flat['region'] -> KeyError: 'region'
['region', 'x', 'y']

Passing aggfunc as a list (["sum"]) or values as a list gives MultiIndex columns like ('sum', 'x'), so pt["x"] fails; select with the full tuple or pass a single string. The index= column becomes the row index, not a column, so flat["region"] fails too; call reset_index() to get it back. Note the sums came back as floats (10.0): region S has no product y, and that NaN cell turns the column into float64.

3. Fix: normalise headers first, fill the gaps

import pandas as pd
df = pd.DataFrame({"region": ["N", "N", "S"], "Product": ["x", "y", "x"],
                   "sales": [10, 20, 5]})
df.columns = df.columns.str.strip().str.lower()
print(df.columns.tolist())
print(df.pivot_table(index="region", columns="product", values="sales",
                     aggfunc="sum", fill_value=0))

Output, identical on pandas 2.3.3 and 3.1.0:

['region', 'product', 'sales']
product   x   y
region         
N        10  20
S         5   0

Stripping and lower-casing the headers once, right after loading, removes both input-side causes. fill_value=0 fills the missing combination and keeps the result as integers. If you get ValueError: Index contains duplicate entries instead, you are calling pivot, not pivot_table: see pivot: Index contains duplicate entries.

Related: KeyError: not in index · merge KeyError · pandas 3 breaking changes.

Frequently asked questions

Why does pivot_table raise KeyError when the column exists?

Usually a case or whitespace mismatch in the header. Run df.columns = df.columns.str.strip() (and .str.lower() if needed) right after loading.

Why can't I select a column from my pivot table result?

With aggfunc or values passed as a list the columns are a MultiIndex, e.g. ('sum', 'x'); select with the tuple. The index= column is the row index; use reset_index() to make it a column again.

Why are my pivot_table sums floats?

Missing combinations become NaN, which forces float64. Pass fill_value=0 to keep integers.

Written with AI assistance. Measured on 2026-10-23 in the python:3-alpine Docker image (Python 3.14.7) with pandas 2.3.3 and pandas 3.1.0. Outputs pasted unedited. PlainTables is our extension; Python and pandas are free.