keras-team/keras · error · ValueError
Argument `metric_variables` must be a list of tensors corres
Error message
Argument `metric_variables` must be a list of tensors corresponding 1:1 to {self.__class__.__name__}().variables. Received list with length {len(metric_variables)}, but expected {len(self.variables)} variables. What it means
Raised by Metric.stateless_update_state when the metric_variables list length differs from the metric's own variables count. Keras 3's stateless metric API requires the caller to pass exactly the tensors previously captured from metric.variables on the same metric object; it is used internally by train_step/test_step.
Source
Thrown at keras/src/metrics/metric.py:123
}
)
def reset_state(self):
"""Reset all of the metric state variables.
This function is called between epochs/steps,
when a metric is evaluated during training.
"""
for v in self.variables:
v.assign(ops.zeros(v.shape, dtype=v.dtype))
def update_state(self, *args, **kwargs):
"""Accumulate statistics for the metric."""
raise NotImplementedError
def stateless_update_state(self, metric_variables, *args, **kwargs):
if len(metric_variables) != len(self.variables):
raise ValueError(
"Argument `metric_variables` must be a list of tensors "
f"corresponding 1:1 to {self.__class__.__name__}().variables. "
f"Received list with length {len(metric_variables)}, but "
f"expected {len(self.variables)} variables."
)
# Gather variable mapping
mapping = list(zip(self.variables, metric_variables))
# Call in stateless scope
with backend.StatelessScope(state_mapping=mapping) as scope:
self.update_state(*args, **kwargs)
# Gather updated variables
metric_variables = []
for v in self.variables:
new_v = scope.get_current_value(v)
if new_v is not None:
metric_variables.append(new_v)View on GitHub (pinned to 7a34a03db6)
Solutions
- Capture and pass the full list from the same metric: vars = list(m.variables).
- Never mix variable lists between metric instances.
- Build the metric (one update_state or explicit build) before snapshotting variables.
Example fix
# before m = keras.metrics.BinaryAccuracy() snap = m.variables # possibly empty/stale m.stateless_update_state([snap[0]], y_true, y_pred) # after m = keras.metrics.BinaryAccuracy() m.update_state(y_true, y_pred) # build variables first snap = list(m.variables) # full 1:1 list m.stateless_update_state(snap, y_true, y_pred)
Defensive patterns
Strategy: validation
Validate before calling
vars_ = list(m.variables) assert len(vars_) == len(metric_variables), 'variable list out of sync' m.stateless_update_state(vars_, *args)
Try / catch
try:
m.stateless_update_state(vars_, y_true, y_pred)
except ValueError:
vars_ = list(m.variables) # re-capture after build
m.stateless_update_state(vars_, y_true, y_pred) Prevention
- Capture m.variables in the same step you use them.
- Build the metric once before snapshotting state.
- Never share variable lists between metric instances.
When it happens
Trigger: Calling m.stateless_update_state(vars_from_other_metric, y_true, y_pred); passing a subset like [v[0]] for a metric with 2+ variables; snapshotting m.variables before the metric built its state (empty vs populated).
Common situations: Custom functional/JAX training loops threading metric state manually; averaging metrics across replicas; variable lists captured at the wrong lifecycle point.
Related errors
- Argument `trainable_variables` must be a list of tensors cor
- Argument `non_trainable_variables` must be a list of tensors
- Layer `add_metric()` method is deprecated. Add your metric i
- Argument `num_thresholds` must be an integer > 0. Received:
- Argument `specificity` must be in the range [0, 1]. Received
AI-assisted analysis of keras-team/keras@7a34a03db6 (2026-08-25).
Data as JSON: /api/errors/1c79be9c96eb9113.
Report an issue: GitHub.