pandas FutureWarning: concatenation with empty or all-NA entries (tested)

FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated usually comes from concatenating onto an empty DataFrame. We ran the same code on pandas 2.3.3 and 3.1.0: on pandas 3 the warning is gone and a float64 column silently becomes object.

1. The warning on pandas 2, the silent change on pandas 3

import warnings
import pandas as pd
warnings.simplefilter("always")

out = pd.DataFrame(columns=["id", "price"])            # empty "accumulator"
new = pd.DataFrame({"id": [1], "price": [9.5]})
res = pd.concat([out, new])
print(res.dtypes.to_dict())

na = pd.DataFrame({"id": [None], "price": [None]})     # all-NA row
res = pd.concat([new, na])
print(res.dtypes.to_dict())

Output with pandas 2.3.3:

/w/b1.py:7: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  res = pd.concat([out, new])
/w/b1.py:11: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  res = pd.concat([new, na])
{'id': dtype('O'), 'price': dtype('float64')}
{'id': dtype('O'), 'price': dtype('float64')}

Output with pandas 3.1.0:

{'id': dtype('O'), 'price': dtype('O')}
{'id': dtype('O'), 'price': dtype('O')}

On pandas 2 the warning fires and price stays float64, because the empty or all-NA frame is ignored when the result dtype is chosen. pandas 3 makes the change the warning announced: the empty frame's object columns now count, so price becomes object with no warning at all. Code that silenced the FutureWarning on pandas 2 can therefore start producing object columns after upgrading.

2. Fix: don't start from an empty DataFrame, and drop all-NA pieces

import warnings
import pandas as pd
warnings.simplefilter("always")

rows = [pd.DataFrame({"id": [i], "price": [i * 1.5]}) for i in range(3)]
res = pd.concat(rows, ignore_index=True)                # fix: collect, concat once
print(res.dtypes.to_dict(), len(res))

out = pd.DataFrame(columns=["id", "price"])
new = pd.DataFrame({"id": [1], "price": [9.5]})
frames = [f for f in (out, new) if not f.empty]         # fix: drop empty frames
print(pd.concat(frames).dtypes.to_dict())

na = pd.DataFrame({"id": [None], "price": [None]})
frames = [f for f in (new, na) if not f.isna().all().all()]   # drop all-NA frames
print(pd.concat(frames).dtypes.to_dict())

Output (identical on pandas 2.3.3 and 3.1.0, no warnings):

{'id': dtype('int64'), 'price': dtype('float64')} 3
{'id': dtype('int64'), 'price': dtype('float64')}
{'id': dtype('int64'), 'price': dtype('float64')}

The usual source is the loop pattern out = pd.DataFrame(columns=[...]) followed by out = pd.concat([out, new]) on each iteration. Collect the pieces in a list and call pd.concat once (also much faster than concatenating in a loop). If empty or all-NA frames can arrive from elsewhere, filter them out first, exactly as the warning text suggests. Suppressing the warning with warnings.filterwarnings does not fix anything: it only hides the dtype change you will get on pandas 3.

Related: all pandas 3 breaking changes · SettingWithCopyWarning · KeyError: not in index · Error tokenizing data.

Frequently asked questions

How do I fix the pandas concat FutureWarning about empty or all-NA entries?

Don't concat onto an empty DataFrame: collect frames in a list and call pd.concat once, and filter out empty or all-NA frames before concatenating.

What changes in pandas 3?

Empty and all-NA frames now count when choosing result dtypes, so a float64 column concatenated with an empty object-dtype frame becomes object, without a warning.

Can I just ignore this FutureWarning?

Hiding it leaves the dtype change in place for pandas 3. Filter the empty or all-NA frames instead.

Written with AI assistance. Measured on 2026-10-23 in the python:3-alpine Docker image (Python 3.14) with pandas 2.3.3 and pandas 3.1.0. Each block was run verbatim on both versions with warnings.simplefilter("always") and the output pasted unedited. PlainHeaders is our extension; Python and pandas are free.