microsoft/qlib · error · NotImplementedError

Please implement the `save_objects` method.

Error message

Please implement the `save_objects` method.

What it means

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.

Source

Thrown at qlib/workflow/recorder.py:88

    def set_recorder_name(self, rname):
        self.recorder_name = rname

    def save_objects(self, local_path=None, artifact_path=None, **kwargs):
        """
        Save objects such as prediction file or model checkpoints to the artifact URI. User
        can save object through keywords arguments (name:value).

        Please refer to the docs of qlib.workflow:R.save_objects

        Parameters
        ----------
        local_path : str
            if provided, them save the file or directory to the artifact URI.
        artifact_path=None : str
            the relative path for the artifact to be stored in the URI.
        """
        raise NotImplementedError(f"Please implement the `save_objects` method.")

    def load_object(self, name):
        """
        Load objects such as prediction file or model checkpoints.

        Parameters
        ----------
        name : str
            name of the file to be loaded.

        Returns
        -------
        The saved object.
        """
        raise NotImplementedError(f"Please implement the `load_object` method.")

    def start_run(self):
        """

View on GitHub (pinned to 79633dd950)

Solutions

  1. Use the standard path: R.start(experiment_name=...) inside qlib.init context so you get an MLflowRecorder, whose save_objects works
  2. 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, ...)
  3. Check how the recorder was obtained — R.get_recorder() on an active run returns a concrete MLflowRecorder; constructing Recorder(...) by hand does not

Example fix

# before
from qlib.workflow.recorder import Recorder
rec = Recorder(...)          # abstract
rec.save_objects(pred=pred)  # NotImplementedError

# after
with R.start('my_exp'):
    rec = R.get_recorder()
    rec.save_objects(pred=pred)
Defensive patterns

Strategy: type-guard

Validate before calling

from qlib.workflow.recorder import Recorder
from qlib.workflow import R

def get_live_recorder():
    with R.start(experiment_name='my_exp'):
        rec = R.get_recorder()
    assert not isinstance(rec, type(None)) and type(rec).save_objects is not Recorder.save_objects
    return rec

Type guard

from qlib.workflow.recorder import Recorder

def is_concrete_recorder(rec) -> bool:
    return type(rec).save_objects is not Recorder.save_objects

Try / catch

try:
    rec.save_objects(pred=pred)
except NotImplementedError as e:
    raise TypeError(f'{type(rec).__name__} is the abstract Recorder; obtain it via R.start/R.get_recorder') from e

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/552f2683f7ed460b. Report an issue: GitHub.