2 AM PAGER

Airflow 3.3.2 shipped an upgrade trap, and the error message lies.

pandas 3 renames the DataFrame class path and XComs record the old one. Upgrade Airflow on every worker first, or the error message will send you chasing your config.

2026-09-23

Airflow 3.3.2 shipped with an upgrade trap, and the error message lies to you.

Here is the trap. pandas 3 renames the DataFrame class path: pandas.DataFrame instead of pandas.core.frame.DataFrame. XComs record that class name in the metadata database. If pandas 3 reaches any worker before Airflow 3.3.2 reaches all of them, every DataFrame pull fails with:

ImportError: pandas.DataFrame was not found in allow list
for deserialization imports.

The message points at your config. It is lying. Changing allowed_deserialization_classes does nothing, because the rows are not corrupt and your config is not wrong. The rows read fine the moment the reader is upgraded. The problem is version skew between your workers, and the error is blaming your allowlist for it.

Upgrade order is the whole game

ALL
workers. Airflow 3.3.2 must reach every component, workers especially, before pandas 3 touches anything. One laggard poisons every DataFrame pull.

The fix is sequencing, not configuration. Roll Airflow 3.3.2 to every component first. Then let pandas 3 in. The order is the entire fix, which is why the misleading error message is so expensive: it sends you into config diffs and allowlist tuning when the answer is a rollout order.

This is a data engineering failure wearing an orchestration costume. Version skew across workers, a metadata store that records assumptions about the runtime, and an error that describes the symptom instead of the cause. You have debugged this shape before.

Downgrades are a one-way door

Rolling back is the second trap. Once pandas 3 has written XComs with the new class path, downgrading strands them. The old reader cannot deserialize the new names. You are stuck forward until you roll forward again, which is another way of saying the downgrade is not a downgrade. It is a door that only opens one way.

Plan the rollback before the upgrade, and make the plan “we do not roll back, we roll forward.” If that sentence makes you uncomfortable, you are not ready to upgrade.

Audit your dtype checks

While you are in there, pandas 3 changes the small things that break the quiet code. String columns come back as str, not object. Missing values come back as nan, not None. Every dtype check, every is None guard, every dtype == object branch in your DAGs deserves a look before the upgrade, not after the 2 AM page.

Bonus: the credential that fails loud

Buried in the same release: an explicit Bearer or OAuth credential now beats the session cookie on the same request, and an expired Bearer fails loud instead of silently riding the cookie.

That second half is the real gift. Silent auth fallback is how you get a request that looks authenticated and is not. Failing loud is the correct behavior, and it is worth upgrading for even if you skip the rest.

The trap, the door, and the lying error message are the headline. But the quiet theme of 3.3.2 is the one this site keeps repeating: the fix is never where the error points. It is in the ordering, the versioning, and the assumptions you recorded in a database six months ago.

This post started as an Instagram post →

Discussion

Talk it through

Argue with us on Instagram.