{"record":{"id":"b1b8efcbf0d3c6fc","repo":"microsoft/qlib","slug":"unknown-metric-s-b1b8ef","errorCode":null,"errorMessage":"unknown metric `%s`","messagePattern":"unknown metric `(.+?)`","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/contrib/model/pytorch_krnn.py","lineNumber":367,"sourceCode":"    def mse(self, pred, label):\r\n        loss = (pred - label) ** 2\r\n        return torch.mean(loss)\r\n\r\n    def loss_fn(self, pred, label):\r\n        mask = ~torch.isnan(label)\r\n\r\n        if self.loss == \"mse\":\r\n            return self.mse(pred[mask], label[mask])\r\n\r\n        raise ValueError(\"unknown loss `%s`\" % self.loss)\r\n\r\n    def metric_fn(self, pred, label):\r\n        mask = torch.isfinite(label)\r\n\r\n        if self.metric in (\"\", \"loss\"):\r\n            return -self.loss_fn(pred[mask], label[mask])\r\n\r\n        raise ValueError(\"unknown metric `%s`\" % self.metric)\r\n\r\n    def get_daily_inter(self, df, shuffle=False):\r\n        # organize the train data into daily batches\r\n        daily_count = df.groupby(level=0, group_keys=False).size().values\r\n        daily_index = np.roll(np.cumsum(daily_count), 1)\r\n        daily_index[0] = 0\r\n        if shuffle:\r\n            # shuffle data\r\n            daily_shuffle = list(zip(daily_index, daily_count))\r\n            np.random.shuffle(daily_shuffle)\r\n            daily_index, daily_count = zip(*daily_shuffle)\r\n        return daily_index, daily_count\r\n\r\n    def train_epoch(self, x_train, y_train):\r\n        x_train_values = x_train.values\r\n        y_train_values = np.squeeze(y_train.values)\r\n        self.krnn_model.train()\r\n\r","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/contrib/model/pytorch_krnn.py#L349-L385","documentation":"KRNNModel.metric_fn supports metric values in ('', 'loss') — i.e. the empty string or 'loss', both meaning 'use negative loss as the score'. Note this file uses the correct `in` check, unlike IGMTFModel's buggy tuple comparison. Any other value (e.g. 'ic') raises ValueError.","triggerScenarios":"KRNNModel(metric='ic') or any string other than ''/'loss', then fit() — the error surfaces when the validation score is computed.","commonSituations":"Copying metric='ic' from HIST/IGMTF workflows where 'ic' is the default; expecting IC-based early stopping on a model that only implements loss-as-metric.","solutions":["Use metric='' or metric='loss' for KRNNModel","If you need 'ic', subclass KRNNModel and add an IC branch to metric_fn"],"exampleFix":"# before\nKRNNModel(metric=\"ic\")  # not supported by krnn\n\n# after\nKRNNModel(metric=\"loss\")","handlingStrategy":"validation","validationCode":"assert metric in (\"\", \"loss\"), \"KRNNModel metric must be '' or 'loss' (no 'ic' support)\"","typeGuard":"def is_supported_krnn_metric(name: str) -> bool:\n    return name in (\"\", \"loss\")","tryCatchPattern":null,"preventionTips":["Do not copy metric='ic' from other qlib models into KRNN configs","Document per-model metric allowlists in your experiment configs"],"tags":["qlib","krnn","metric","invalid-argument"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}