{"record":{"id":"636479626c737a9e","repo":"microsoft/qlib","slug":"only-processors-usable-for-inference-can-be-used-i","errorCode":null,"errorMessage":"Only processors usable for inference can be used in `infer_processors` ","messagePattern":"Only processors usable for inference can be used in `infer_processors` ","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"qlib/data/dataset/handler.py","lineNumber":535,"sourceCode":"        for proc in self.get_all_processors():\n            with TimeInspector.logt(f\"{proc.__class__.__name__}\"):\n                proc.fit(self._data)\n\n    def fit_process_data(self):\n        \"\"\"\n        fit and process data\n\n        The input of the `fit` will be the output of the previous processor\n        \"\"\"\n        self.process_data(with_fit=True)\n\n    @staticmethod\n    def _run_proc_l(\n        df: pd.DataFrame, proc_l: List[processor_module.Processor], with_fit: bool, check_for_infer: bool\n    ) -> pd.DataFrame:\n        for proc in proc_l:\n            if check_for_infer and not proc.is_for_infer():\n                raise TypeError(\"Only processors usable for inference can be used in `infer_processors` \")\n            with TimeInspector.logt(f\"{proc.__class__.__name__}\"):\n                if with_fit:\n                    proc.fit(df)\n                df = proc(df)\n        return df\n\n    @staticmethod\n    def _is_proc_readonly(proc_l: List[processor_module.Processor]):\n        \"\"\"\n        NOTE: it will return True if `len(proc_l) == 0`\n        \"\"\"\n        for p in proc_l:\n            if not p.readonly():\n                return False\n        return True\n\n    def process_data(self, with_fit: bool = False):\n        \"\"\"","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/data/dataset/handler.py#L517-L553","documentation":"DataHandlerLP runs `infer_processors` on data that is also served at inference time. Processors declare themselves inference-safe via `is_for_infer()`; those that leak future information (e.g. processors derived for labels, `is_for_infer() == False`) are forbidden in that list and raise TypeError with a trailing space in the message.","triggerScenarios":"Configuring DataHandlerLP with `infer_processors=[SomeLearnOnlyProcessor()]` where the processor's class sets `is_for_infer = False` (or is_for_infer() returns False) — commonly label-related or fit-on-full-data processors.","commonSituations":"Copy-pasting a processor list from `learn_processors` into `infer_processors` in workflow configs; processors that normalize using statistics of the full period.","solutions":["Move the offending processor to `learn_processors`.","If the processor is genuinely inference-safe, override `is_for_infer()` in its class to return True after verifying it uses no future data."],"exampleFix":"# before (config)\ndata_handler_config = {\n    'infer_processors': ['CorrProcessor'],   # learn-only\n    'learn_processors': ['RobustZScoreNorm'],\n}\n\n# after\ndata_handler_config = {\n    'infer_processors': ['RobustZScoreNorm'],\n    'learn_processors': ['CorrProcessor'],\n}","handlingStrategy":"validation","validationCode":"def all_infer_safe(proc_l) -> bool:\n    return all(p.is_for_infer() for p in proc_l)\n\n# before DataHandlerLP setup\nassert all_infer_safe(handler_config['infer_processors']), 'infer_processors contain learn-only processors'","typeGuard":"def is_infer_safe(proc) -> bool:\n    return bool(proc.is_for_infer())","tryCatchPattern":null,"preventionTips":["Keep label/learn-only processors exclusively in learn_processors.","When writing custom processors, implement is_for_infer() honestly."],"tags":["processors","data-leakage","config"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}