{"record":{"id":"1c79be9c96eb9113","repo":"keras-team/keras","slug":"argument-metric-variables-must-be-a-list-of-tens","errorCode":null,"errorMessage":"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.","messagePattern":"Argument `metric_variables` must be a list of tensors corresponding 1:1 to (.+?)\\(\\)\\.variables\\. Received list with length (.+?), but expected (.+?) variables\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"keras/src/metrics/metric.py","lineNumber":123,"sourceCode":"            }\n        )\n\n    def reset_state(self):\n        \"\"\"Reset all of the metric state variables.\n\n        This function is called between epochs/steps,\n        when a metric is evaluated during training.\n        \"\"\"\n        for v in self.variables:\n            v.assign(ops.zeros(v.shape, dtype=v.dtype))\n\n    def update_state(self, *args, **kwargs):\n        \"\"\"Accumulate statistics for the metric.\"\"\"\n        raise NotImplementedError\n\n    def stateless_update_state(self, metric_variables, *args, **kwargs):\n        if len(metric_variables) != len(self.variables):\n            raise ValueError(\n                \"Argument `metric_variables` must be a list of tensors \"\n                f\"corresponding 1:1 to {self.__class__.__name__}().variables. \"\n                f\"Received list with length {len(metric_variables)}, but \"\n                f\"expected {len(self.variables)} variables.\"\n            )\n        # Gather variable mapping\n        mapping = list(zip(self.variables, metric_variables))\n\n        # Call in stateless scope\n        with backend.StatelessScope(state_mapping=mapping) as scope:\n            self.update_state(*args, **kwargs)\n\n        # Gather updated variables\n        metric_variables = []\n        for v in self.variables:\n            new_v = scope.get_current_value(v)\n            if new_v is not None:\n                metric_variables.append(new_v)","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/keras-team/keras/blob/7a34a03db60bf60042242d6a556fc3be119046a5/keras/src/metrics/metric.py#L105-L141","documentation":"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.","triggerScenarios":"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).","commonSituations":"Custom functional/JAX training loops threading metric state manually; averaging metrics across replicas; variable lists captured at the wrong lifecycle point.","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."],"exampleFix":"# before\nm = keras.metrics.BinaryAccuracy()\nsnap = m.variables                  # possibly empty/stale\nm.stateless_update_state([snap[0]], y_true, y_pred)\n\n# after\nm = keras.metrics.BinaryAccuracy()\nm.update_state(y_true, y_pred)      # build variables first\nsnap = list(m.variables)             # full 1:1 list\nm.stateless_update_state(snap, y_true, y_pred)","handlingStrategy":"validation","validationCode":"vars_ = list(m.variables)\nassert len(vars_) == len(metric_variables), 'variable list out of sync'\nm.stateless_update_state(vars_, *args)","typeGuard":null,"tryCatchPattern":"try:\n    m.stateless_update_state(vars_, y_true, y_pred)\nexcept ValueError:\n    vars_ = list(m.variables)  # re-capture after build\n    m.stateless_update_state(vars_, y_true, y_pred)","preventionTips":["Capture m.variables in the same step you use them.","Build the metric once before snapshotting state.","Never share variable lists between metric instances."],"tags":["keras","metrics","stateless-api","variables"],"backgroundTag":"api-contract-violation","analyzedSha":"7a34a03db60bf60042242d6a556fc3be119046a5","analyzedAt":"2026-08-25T21:25:25.994Z","schemaVersion":2},"datasetVersion":"2026-08-26T02:17:13.382Z"}