{"record":{"id":"552f2683f7ed460b","repo":"microsoft/qlib","slug":"please-implement-the-save-objects-method","errorCode":null,"errorMessage":"Please implement the `save_objects` method.","messagePattern":"Please implement the `save_objects` method\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/workflow/recorder.py","lineNumber":88,"sourceCode":"\r\n    def set_recorder_name(self, rname):\r\n        self.recorder_name = rname\r\n\r\n    def save_objects(self, local_path=None, artifact_path=None, **kwargs):\r\n        \"\"\"\r\n        Save objects such as prediction file or model checkpoints to the artifact URI. User\r\n        can save object through keywords arguments (name:value).\r\n\r\n        Please refer to the docs of qlib.workflow:R.save_objects\r\n\r\n        Parameters\r\n        ----------\r\n        local_path : str\r\n            if provided, them save the file or directory to the artifact URI.\r\n        artifact_path=None : str\r\n            the relative path for the artifact to be stored in the URI.\r\n        \"\"\"\r\n        raise NotImplementedError(f\"Please implement the `save_objects` method.\")\r\n\r\n    def load_object(self, name):\r\n        \"\"\"\r\n        Load objects such as prediction file or model checkpoints.\r\n\r\n        Parameters\r\n        ----------\r\n        name : str\r\n            name of the file to be loaded.\r\n\r\n        Returns\r\n        -------\r\n        The saved object.\r\n        \"\"\"\r\n        raise NotImplementedError(f\"Please implement the `load_object` method.\")\r\n\r\n    def start_run(self):\r\n        \"\"\"\r","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/workflow/recorder.py#L70-L106","documentation":"Recorder is the abstract base of qlib's recorder system (MLflowRecorder is the concrete implementation); save_objects is the method that persists artifacts (prediction files, model checkpoints) to the recorder's artifact URI. The base class raises NotImplementedError by design. Hitting it means save_objects was called on the base Recorder — i.e. no concrete backend was ever selected or a custom subclass is incomplete.","triggerScenarios":"Instantiating qlib.workflow.recorder.Recorder directly and calling save_objects; a custom Recorder subclass missing the save_objects override being registered via QlibRecorder.set_uri/register; code that fetched a recorder as the abstract type from a misconfigured exp_manager/recorder factory.","commonSituations":"Attempting to add a new tracking backend to qlib by subclassing Recorder; unit tests using the base class as a stand-in; factory/registration bugs where the MLflowRecorder was not chosen (e.g. bad uri scheme).","solutions":["Use the standard path: R.start(experiment_name=...) inside qlib.init context so you get an MLflowRecorder, whose save_objects works","If subclassing Recorder, implement save_objects(self, local_path=None, artifact_path=None, **kwargs) plus every other abstract method (load_object, start_run, end_run, log_params, ...)","Check how the recorder was obtained — R.get_recorder() on an active run returns a concrete MLflowRecorder; constructing Recorder(...) by hand does not"],"exampleFix":"# before\nfrom qlib.workflow.recorder import Recorder\nrec = Recorder(...)          # abstract\nrec.save_objects(pred=pred)  # NotImplementedError\n\n# after\nwith R.start('my_exp'):\n    rec = R.get_recorder()\n    rec.save_objects(pred=pred)","handlingStrategy":"type-guard","validationCode":"from qlib.workflow.recorder import Recorder\nfrom qlib.workflow import R\n\ndef get_live_recorder():\n    with R.start(experiment_name='my_exp'):\n        rec = R.get_recorder()\n    assert not isinstance(rec, type(None)) and type(rec).save_objects is not Recorder.save_objects\n    return rec","typeGuard":"from qlib.workflow.recorder import Recorder\n\ndef is_concrete_recorder(rec) -> bool:\n    return type(rec).save_objects is not Recorder.save_objects","tryCatchPattern":"try:\n    rec.save_objects(pred=pred)\nexcept NotImplementedError as e:\n    raise TypeError(f'{type(rec).__name__} is the abstract Recorder; obtain it via R.start/R.get_recorder') from e","preventionTips":["Never instantiate Recorder directly; always get recorders from QlibRecorder (R)","In custom Recorder subclasses, implement every abstract method before registering it","Type-check received recorders at API boundaries in framework code"],"tags":["qlib","recorder","abstract-method","not-implemented"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}