microsoft/qlib · error · NotImplementedError

{type(process)} is not supported in `process_collect`.

Error message

{type(process)} is not supported in `process_collect`.

What it means

Raised in ProcessCollector.process_collect when an element of process_list is not callable. Each entry is invoked as process(value, *args, **kwargs), so non-callable entries (strings, None, wrongly-instantiated objects) are rejected with NotImplementedError.

Source

Thrown at qlib/workflow/task/collect.py:74

        For example, you can group and ensemble.

        Args:
            collected_dict (dict): the dict return by `collect`
            process_list (list or Callable): the list of processors or the instance of a processor to process dict.
                The processor order is the same as the list order.
                For example: [Group1(..., Ensemble1()), Group2(..., Ensemble2())]

        Returns:
            dict: the dict after processing.
        """
        if not isinstance(process_list, list):
            process_list = [process_list]
        result = {}
        for artifact in collected_dict:
            value = collected_dict[artifact]
            for process in process_list:
                if not callable(process):
                    raise NotImplementedError(f"{type(process)} is not supported in `process_collect`.")
                value = process(value, *args, **kwargs)
            result[artifact] = value
        return result

    def __call__(self, *args, **kwargs) -> dict:
        """
        Do the workflow including ``collect`` and ``process_collect``

        Returns:
            dict: the dict after collecting and processing.
        """
        collected = self.collect()
        return self.process_collect(collected, self.process_list, *args, **kwargs)


class MergeCollector(Collector):
    """
    A collector to collect the results of other Collectors

View on GitHub (pinned to 79633dd950)

Solutions

  1. Ensure every element of process_list is callable: pass function objects or instances implementing __call__ (qlib's Ensemble/Group classes are callable).
  2. If the value comes from config, resolve it to a callable first (e.g. partial/init_instance_by_config) before building the ProcessCollector.
  3. Validate process_list up front: `assert all(callable(p) for p in process_list)`.
  4. Do not pass None or strings; remove empty entries from the list.

Example fix

// before
collector = ProcessCollector(process_list=["ensemble"], ...)

// after
from qlib.workflow.task.collect import Ensemble
collector = ProcessCollector(process_list=[Ensemble()], ...)
Defensive patterns

Strategy: type-guard

Validate before calling

process_list = [p for p in process_list if p is not None]
assert all(callable(p) for p in process_list), f"non-callable process: {[p for p in process_list if not callable(p)]}"

Type guard

def is_callable_process(p) -> bool:
    return callable(p)

Prevention

When it happens

Trigger: Passing process_list containing a class name string instead of the class, passing None (e.g. an unset config value), or passing an already-called result instead of a function/callable object: ProcessCollector(..., process_list=[my_func]) where my_func is not callable.

Common situations: Config-driven workflows where the process step comes from YAML/JSON as a string and is never resolved to a callable; passing an Ensemble/Group instance that does not implement __call__; passing a dict of parameters instead of a processor object.

Related errors


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