TensorFlow projects that still depend on Keras 2 behavior can break after a TensorFlow upgrade because tf.keras now loads Keras 3 by default in newer releases. The legacy package keeps older tf.keras code running while a migration to standalone Keras 3 is staged and tested.
The fallback uses the separate tf-keras PyPI package, imported as tf_keras, and the TF_USE_LEGACY_KERAS=1 process switch. TensorFlow checks that switch as it loads, so the variable must be present before the first TensorFlow import in each shell, notebook kernel, worker, or application server process.
Treat the switch as compatibility mode, not a replacement for migration testing. Keep TensorFlow and tf-keras on matching minor releases in locked environments, restart long-running processes after changing the launch environment, and confirm the resolved module path before running production jobs.
$ python -m pip install --upgrade "tensorflow>=2.16" tf-keras Successfully installed tensorflow-2.21.0 tf-keras-2.21.0
For production lock files, pin TensorFlow and tf-keras to the same minor release, such as tensorflow~=2.21.0 and tf-keras~=2.21.0.
Related: How to install Keras with pip
$ python - <<'PY'
import tensorflow as tf
print(f"default_dense_module={tf.keras.layers.Dense.__module__}")
PY
default_dense_module=keras.src.layers.core.dense
If the output already starts with tf_keras, a parent shell, notebook launcher, or service unit is already setting TF_USE_LEGACY_KERAS.
$ export TF_USE_LEGACY_KERAS=1
import os os.environ.setdefault("TF_USE_LEGACY_KERAS", "1") import tensorflow as tf
Set TF_USE_LEGACY_KERAS before import tensorflow, from tensorflow import keras, TensorFlow add-on packages, and project modules that import TensorFlow. A late assignment leaves the running process on the already-loaded Keras path.
Restart notebook kernels, job workers, application servers, and interactive shells. TensorFlow does not move an already imported process from Keras 3 to tf-keras.
$ TF_USE_LEGACY_KERAS=1 python - <<'PY'
import os
import tensorflow as tf
import tf_keras
print(f"tensorflow={tf.__version__}")
print(f"tf_keras={tf_keras.__version__}")
print(f"legacy_switch={os.environ['TF_USE_LEGACY_KERAS']}")
print(f"dense_module={tf.keras.layers.Dense.__module__}")
PY
tensorflow=2.21.0
tf_keras=2.21.0
legacy_switch=1
dense_module=tf_keras.src.layers.core.dense
The tf_keras.src module path proves tf.keras is using the legacy package. Code that imports keras directly still imports standalone Keras 3; change those files to import tf_keras as keras or migrate them to Keras 3.
import os os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "2") os.environ.setdefault("TF_USE_LEGACY_KERAS", "1") import numpy as np import tensorflow as tf model = tf.keras.Sequential( [ tf.keras.Input(shape=(3,), name="features"), tf.keras.layers.Dense(2, activation="softmax", name="score"), ] ) prediction = model(np.ones((1, 3), dtype="float32"), training=False) print(f"keras_module={tf.keras.layers.Dense.__module__.split('.')[0]}") print(f"prediction_shape={tuple(prediction.shape)}")
$ TF_USE_LEGACY_KERAS=1 python legacy_keras_smoke.py keras_module=tf_keras prediction_shape=(1, 2)
keras_module=tf_keras confirms the legacy implementation handled the model call. Replace the small Sequential model with a project entry point or regression test before relying on the compatibility mode for production jobs.
$ rm legacy_keras_smoke.py