{"record":{"id":"09506444f20ab3c6","repo":"keras-team/keras","slug":"invalid-keys-invalid-keys-valid-variable-key","errorCode":null,"errorMessage":"Invalid keys: \"{invalid_keys}\". Valid variable key options are: \"{list(ConfusionMatrix)}\"","messagePattern":"Invalid keys: \"(.+?)\"\\. Valid variable key options are: \"(.+?)\"","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"keras/src/metrics/metrics_utils.py","lineNumber":455,"sourceCode":"        # details.\n        thresholds_with_epsilon = thresholds[0] < 0.0 or thresholds[-1] > 1.0\n\n    thresholds = ops.convert_to_tensor(thresholds, dtype=variable_dtype)\n    num_thresholds = ops.shape(thresholds)[0]\n\n    if multi_label:\n        one_thresh = ops.equal(\n            np.array(1, dtype=\"int32\"),\n            len(thresholds.shape),\n        )\n    else:\n        one_thresh = np.array(True, dtype=\"bool\")\n\n    invalid_keys = [\n        key for key in variables_to_update if key not in list(ConfusionMatrix)\n    ]\n    if invalid_keys:\n        raise ValueError(\n            f'Invalid keys: \"{invalid_keys}\". '\n            f'Valid variable key options are: \"{list(ConfusionMatrix)}\"'\n        )\n\n    y_pred, y_true = squeeze_or_expand_to_same_rank(y_pred, y_true)\n    if sample_weight is not None:\n        sample_weight = ops.expand_dims(\n            ops.cast(sample_weight, dtype=variable_dtype), axis=-1\n        )\n        _, sample_weight = squeeze_or_expand_to_same_rank(\n            y_true, sample_weight, expand_rank_1=False\n        )\n\n    if top_k is not None:\n        y_pred = _filter_top_k(y_pred, top_k)\n\n    if class_id is not None:\n        if len(y_pred.shape) == 1:","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/keras-team/keras/blob/7a34a03db60bf60042242d6a556fc3be119046a5/keras/src/metrics/metrics_utils.py#L437-L473","documentation":"The counterpart of the empty-dict check: if variables_to_update contains any key not in the ConfusionMatrix enum, update_confusion_matrix_variables() raises this ValueError listing the invalid keys. Mixed dicts (some valid, some invalid keys) are rejected too - validity is all-or-nothing.","triggerScenarios":"Calling update_confusion_matrix_variables(variables_to_update={ConfusionMatrix.TP: v, 'recall': r}) - an extraneous key alongside valid ones.","commonSituations":"Extending a copied metric implementation by putting extra state keys into the same dict instead of separate variables.","solutions":["Keep only ConfusionMatrix enum keys in variables_to_update; maintain extra state in separate metric variables updated outside this call.","Re-check for typos or string/enum mixing after refactoring."],"exampleFix":"# before\nvars_ = {ConfusionMatrix.TP: self.tp, 'custom': self.custom_var}\nmetrics_utils.update_confusion_matrix_variables(vars_, y_true, y_pred)\n\n# after\nvars_ = {ConfusionMatrix.TP: self.tp}\nmetrics_utils.update_confusion_matrix_variables(vars_, y_true, y_pred)\nself.custom_var.update(custom_op)  # update extra state separately","handlingStrategy":"type-guard","validationCode":"from keras.src.metrics.metrics_utils import ConfusionMatrix\ndef all_keys_valid(d):\n    return all(k in list(ConfusionMatrix) for k in (d or {}))","typeGuard":"def is_valid_cm_dict(d) -> bool:\n    from keras.src.metrics.metrics_utils import ConfusionMatrix\n    return all(k in list(ConfusionMatrix) for k in (d or {}))","tryCatchPattern":null,"preventionTips":["Keep extra metric state in separate variables, never inside variables_to_update."],"tags":["keras","metrics","confusion-matrix","dict-keys","internal-api"],"backgroundTag":"invalid-dict-key","analyzedSha":"7a34a03db60bf60042242d6a556fc3be119046a5","analyzedAt":"2026-08-25T21:25:25.994Z","schemaVersion":2},"datasetVersion":"2026-08-26T02:17:13.382Z"}