microsoft/qlib · error · NotImplementedError

Please implement the `save_objs` method

Error message

Please implement the `save_objs` method

What it means

save_objs is the batch-write method of the abstract ObjManager interface (base FileManager implements it as a loop over save_obj). The base class stub raises NotImplementedError to force concrete subclasses to provide bulk persistence. Hitting it means batch-saving went to the abstract base or to a subclass that only implemented single-object saving.

Source

Thrown at qlib/utils/objm.py:34

        Parameters
        ----------
        obj : object
            object to be saved
        name : str
            name of the object
        """
        raise NotImplementedError(f"Please implement `save_obj`")

    def save_objs(self, obj_name_l):
        """
        save objects

        Parameters
        ----------
        obj_name_l : list of <obj, name>
        """
        raise NotImplementedError(f"Please implement the `save_objs` method")

    def load_obj(self, name: str) -> object:
        """
        load object by name

        Parameters
        ----------
        name : str
            the name of the object

        Returns
        -------
        object:
            loaded object
        """
        raise NotImplementedError(f"Please implement the `load_obj` method")

    def exists(self, name: str) -> bool:

View on GitHub (pinned to 79633dd950)

Solutions

  1. Switch to FileManager (its save_objs delegates to save_obj and works out of the box).
  2. Implement save_objs(self, obj_name_l) in your subclass — delegating to self.save_obj(obj, name) in a loop is sufficient.
  3. As a workaround, call save_obj per item instead of save_objs.

Example fix

// before
mgr = ObjManager(); mgr.save_objs([(m, 'm1')])  # NotImplementedError

// after
class MyManager(ObjManager):
    def save_obj(self, obj, name):
        backend.put(name, obj)
    def save_objs(self, obj_name_l):
        for obj, name in obj_name_l:
            self.save_obj(obj, name)
Defensive patterns

Strategy: fallback

Validate before calling

if not hasattr(type(mgr), 'save_objs') or type(mgr).save_objs is ObjManager.save_objs:
    for obj, name in items:
        mgr.save_obj(obj, name)  # fallback to per-item saves
else:
    mgr.save_objs(items)

Type guard

def can_batch_save(mgr) -> bool:
    return type(mgr).save_objs is not ObjManager.save_objs

Try / catch

try:
    mgr.save_objs(items)
except NotImplementedError:
    for obj, name in items:
        mgr.save_obj(obj, name)

Prevention

When it happens

Trigger: ObjManager().save_objs([(obj1, 'a'), (obj2, 'b')]) or a custom manager class overriding save_obj/load_obj but not save_objs.

Common situations: Custom S3/Redis-backed object managers written against an older qlib interface; code paths in qlib's task/workflow machinery that dump many artifacts at once; partial test doubles that stub only the methods a previous test used.

Related errors


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