KeyError
Keras
TensorFlow
Deep Learning
Python

How to resolve KeyError 'val_mean_absolute_error' Keras 2.3.1 and TensorFlow 2.0 From Chollet Deep Learning with Python

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This KeyError happens when your code tries to read a metric from history.history using a name that is not actually present. In older Keras and early TensorFlow 2 combinations, metric key names changed depending on how the metric was specified, so code copied from a book or notebook often asked for val_mean_absolute_error while the training history actually stored something like val_mae.

Why the Error Happens

When you call model.fit(...), Keras returns a History object. Its history field is a dictionary of metric names to recorded values.

Example:

python
history = model.fit(...)
print(history.history.keys())

If your code later does this:

python
history.history["val_mean_absolute_error"]

but that key is not in the dictionary, Python raises:

text
KeyError: 'val_mean_absolute_error'

So the immediate fix is not guesswork. It is to inspect the keys that actually exist.

Inspect the Available History Keys First

A minimal example:

python
1import tensorflow as tf
2from tensorflow import keras
3from tensorflow.keras import layers
4import numpy as np
5
6x = np.random.rand(200, 4)
7y = np.random.rand(200, 1)
8
9model = keras.Sequential([
10    layers.Dense(16, activation="relu", input_shape=(4,)),
11    layers.Dense(1),
12])
13
14model.compile(optimizer="adam", loss="mse", metrics=["mae"])
15
16history = model.fit(x, y, validation_split=0.2, epochs=2, verbose=0)
17print(history.history.keys())

Typical output may include keys such as:

text
dict_keys(['loss', 'mae', 'val_loss', 'val_mae'])

In that case, the correct key is val_mae, not val_mean_absolute_error.

Use the Key That Matches Your Compile Call

If you compiled like this:

python
model.compile(optimizer="adam", loss="mse", metrics=["mae"])

then your plotting code should use:

python
val_metric = history.history["val_mae"]

If you compiled like this:

python
1model.compile(
2    optimizer="adam",
3    loss="mse",
4    metrics=[tf.keras.metrics.MeanAbsoluteError(name="mean_absolute_error")],
5)

then the history key is more likely to be:

python
history.history["val_mean_absolute_error"]

That is the core rule: metric names in the history dictionary come from how the metric was registered.

A Safer Plotting Pattern

Instead of hardcoding one metric name from memory, inspect the available keys and choose the correct one:

python
1metric_name = None
2for candidate in ["val_mean_absolute_error", "val_mae"]:
3    if candidate in history.history:
4        metric_name = candidate
5        break
6
7if metric_name is None:
8    raise ValueError(f"Validation metric not found. Available keys: {list(history.history.keys())}")
9
10print(history.history[metric_name])

This makes notebooks and teaching examples more resilient across Keras versions.

Validation Metrics Exist Only If You Logged Validation Data

Another reason the key can be missing is that no validation metrics were recorded at all. If you never passed validation data or a validation split, there will be no val_... entries.

For example, this will not create val_* keys:

python
history = model.fit(x, y, epochs=5)

But this will:

python
history = model.fit(x, y, validation_split=0.2, epochs=5)

So before worrying about naming differences, make sure validation metrics were actually requested during training.

Why Book Code Sometimes Breaks

Examples from books and older tutorials often reflect one exact package combination. Later environments can still train the model successfully while changing the metric key names slightly.

Common variations include:

  • 'mae versus mean_absolute_error'
  • 'acc versus accuracy'
  • 'val_mae versus val_mean_absolute_error'

This is why printing history.history.keys() is usually the first debugging step for any Keras plotting error.

A Clean Modern Pattern

A robust training and plotting snippet looks like this:

python
1import matplotlib.pyplot as plt
2
3history = model.fit(x, y, validation_split=0.2, epochs=10, verbose=0)
4print(history.history.keys())
5
6train_key = "mae" if "mae" in history.history else "mean_absolute_error"
7val_key = "val_mae" if "val_mae" in history.history else "val_mean_absolute_error"
8
9plt.plot(history.history[train_key], label=train_key)
10plt.plot(history.history[val_key], label=val_key)
11plt.legend()
12plt.show()

That keeps the example compatible with the actual history keys rather than assuming one exact metric name.

Common Pitfalls

The biggest pitfall is hardcoding val_mean_absolute_error without checking what keys history.history actually contains.

Another common issue is compiling with a shorthand metric such as "mae" and then expecting the long-form metric name in the history dictionary.

People also often forget to pass validation data, then wonder why every val_* key is missing. No validation input means no validation metrics.

Finally, do not debug this as if the model is failing to train. In most cases the training worked fine. The error is in the code that reads the training history afterward.

Summary

  • The error means the requested metric key is not present in history.history.
  • Print history.history.keys() first to see the actual metric names.
  • 'val_mae and val_mean_absolute_error are both plausible depending on how the metric was defined.'
  • Validation keys exist only if you used validation data or validation_split.
  • The safest fix is to select the metric name based on the keys actually recorded.

Course illustration
Course illustration

All Rights Reserved.