microsoft/qlib · error · ValueError

This RecordTemp did not set recorder yet.

Error message

This RecordTemp did not set recorder yet.

What it means

RecordTemp.recorder is a property that raises ValueError when self._recorder is None. RecordTemp (and subclasses like SignalRecord, PortAnaRecord) must be bound to a QlibRecorder to know where to save artifacts; a None recorder means generate/save/load cannot proceed, so the property fails fast.

Source

Thrown at qlib/workflow/record_temp.py:65

        return "/".join(names)

    def save(self, **kwargs):
        """
        It behaves the same as self.recorder.save_objects.
        But it is an easier interface because users don't have to care about `get_path` and `artifact_path`
        """
        art_path = self.get_path()
        if art_path == "":
            art_path = None
        self.recorder.save_objects(artifact_path=art_path, **kwargs)

    def __init__(self, recorder):
        self._recorder = recorder

    @property
    def recorder(self):
        if self._recorder is None:
            raise ValueError("This RecordTemp did not set recorder yet.")
        return self._recorder

    def generate(self, **kwargs):
        """
        Generate certain records such as IC, backtest etc., and save them.

        Parameters
        ----------
        kwargs

        Return
        ------
        """
        raise NotImplementedError(f"Please implement the `generate` method.")

    def load(self, name: str, parents: bool = True):
        """
        It behaves the same as self.recorder.load_object.

View on GitHub (pinned to 79633dd950)

Solutions

  1. Create/start a recorder first (R.start(...) or R.get_recorder(...)) and pass the live recorder into the record temp constructor
  2. Guard the lookup: only build the RecordTemp when the recorder is not None
  3. Check the 'record' section of your workflow config — record classes listed there are instantiated with the active recorder; make sure a run is active

Example fix

# before
with R.start('my_exp'):
    pass
rec = R.get_recorder()          # may be None if no run matched
sr = SignalRecord(recorder=rec) # rec is None -> later ValueError

# after
with R.start('my_exp'):
    rec = R.get_recorder()
    assert rec is not None, 'no active recorder'
    sr = SignalRecord(recorder=rec)
    sr.generate()
Defensive patterns

Strategy: validation

Validate before calling

from qlib.workflow import R

def get_record(recorder, record_cls):
    if recorder is None:
        raise ValueError('no active recorder; call R.start(...) first')
    return record_cls(recorder=recorder)

Type guard

def is_live_recorder(rec) -> bool:
    return rec is not None and hasattr(rec, 'save_objects') and getattr(rec, 'id', None) is not None

Try / catch

try:
    rt.recorder  # property access
except ValueError as e:
    raise RuntimeError('record temp has no recorder; recreate it inside R.start context') from e

Prevention

When it happens

Trigger: Constructing RecordTemp(recorder=None) or a subclass whose __init__ propagates recorder=None; using a record template before R.start() created a recorder; passing the result of a failed R.get_recorder() call; deserializing/pickling a record temp without its recorder.

Common situations: Creating record objects manually outside a with R.start(...) context; recorder lookup returning None and being passed through unchecked; forgetting to attach a recorder when composing custom record lists for the 'record' field in a workflow config.

Related errors


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