{"record":{"id":"f6a413bab3ab2a06","repo":"microsoft/qlib","slug":"operator-must-be-subclass-of-expressionops-not","errorCode":null,"errorMessage":"operator must be subclass of ExpressionOps, not {}","messagePattern":"operator must be subclass of ExpressionOps, not (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"qlib/data/ops.py","lineNumber":1653,"sourceCode":"            - if type(ops_list) is List[dict], each element of ops_list represents the config of operator, which has the following format:\n\n                .. code-block:: text\n\n                    {\n                        \"class\": class_name,\n                        \"module_path\": path,\n                    }\n\n                Note: `class` should be the class name of operator, `module_path` should be a python module or path of file.\n        \"\"\"\n        for _operator in ops_list:\n            if isinstance(_operator, dict):\n                _ops_class, _ = get_callable_kwargs(_operator)\n            else:\n                _ops_class = _operator\n\n            if not issubclass(_ops_class, (Expression,)):\n                raise TypeError(\"operator must be subclass of ExpressionOps, not {}\".format(_ops_class))\n\n            if _ops_class.__name__ in self._ops:\n                get_module_logger(self.__class__.__name__).warning(\n                    \"The custom operator [{}] will override the qlib default definition\".format(_ops_class.__name__)\n                )\n            self._ops[_ops_class.__name__] = _ops_class\n\n    def __getattr__(self, key):\n        if key not in self._ops:\n            raise AttributeError(\"The operator [{0}] is not registered\".format(key))\n        return self._ops[key]\n\n\nOperators = OpsWrapper()\n\n\ndef register_all_ops(C):\n    \"\"\"register all operator\"\"\"","sourceCodeStart":1635,"sourceCodeEnd":1671,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/data/ops.py#L1635-L1671","documentation":"Raised by Operators.register_custom_ops (qlib/data/ops.py, OpsWrapper) when an entry in the ops_list is not a subclass of qlib's Expression class. Custom operators must extend Expression (or a subclass like ElemOperator/Rolling) so they can participate in expression trees; passing a plain function, an instance instead of a class, or a config whose 'class' resolves to a non-Expression callable triggers this TypeError. (The message text says 'ExpressionOps', an older name; the check is against Expression.)","triggerScenarios":"Operators.register_custom_ops([my_func]) where my_func is a def; registering a class that inherits only from object; passing a dict config {\"class\": \"MyOp\", \"module_path\": ...} where MyOp does not subclass Expression; passing an instance MyOp() instead of the class MyOp.","commonSituations":"Extending qlib with user-defined operators in notebooks or plugins; following outdated docs that referenced the old ExpressionOps base class name; accidentally passing an instance rather than the class object.","solutions":["Make the custom operator a class inheriting from qlib.data.ops.Expression (or a fitting subclass such as Rolling or PairOperator) and implement _load_internal.","Register the class, not an instance: Operators.register_custom_ops([MyOp]).","If using a dict config, ensure \"class\" names the operator class and \"module_path\" points to the module/file that defines that Expression subclass."],"exampleFix":"# before\ndef double(x):\n    return x * 2\nOperators.register_custom_ops([double])  # TypeError\n\n# after\nfrom qlib.data.ops import Expression\n\nclass Double(Expression):\n    def __init__(self, feature):\n        self.feature = feature\n    def _load_internal(self, instrument, start_index, end_index, freq):\n        series = self.feature.load(instrument, start_index, end_index, freq)\n        return series * 2\n    def get_extended_window_size(self):\n        return self.feature.get_extended_window_size()\n\nOperators.register_custom_ops([Double])","handlingStrategy":"type-guard","validationCode":"from qlib.data.ops import Expression\nfor op in ops_list:\n    cls = op[\"class\"] if isinstance(op, dict) else op\n    if not (isinstance(cls, type) and issubclass(cls, Expression)):\n        raise TypeError(f\"{cls} is not an Expression subclass\")","typeGuard":"def is_expression_subclass(obj) -> bool:\n    return isinstance(obj, type) and issubclass(obj, Expression)","tryCatchPattern":null,"preventionTips":["Always define custom operators as classes deriving from qlib.data.ops.Expression.","Register the class object, never an instance.","Unit-test registration with a trivial expression evaluation right after register_custom_ops."],"tags":["qlib","operators","extension","type-error"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}