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 CollectorsView on GitHub (pinned to 79633dd950)
Solutions
- Ensure every element of process_list is callable: pass function objects or instances implementing __call__ (qlib's Ensemble/Group classes are callable).
- If the value comes from config, resolve it to a callable first (e.g. partial/init_instance_by_config) before building the ProcessCollector.
- Validate process_list up front: `assert all(callable(p) for p in process_list)`.
- 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
- Pass function/callable objects (e.g. Ensemble()) in process_list, never strings from config.
- Resolve config strings to callables via init_instance_by_config before constructing ProcessCollector.
- Unit-test collectors with a dummy callable to catch config regressions early.
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
- {freq} is not supported in NumpyQuote
- {method} is not supported
- Invalid Qlib configuration (note: the global config has alre
- provider_uri cannot be None
- risk_analysis accumulation mode {mode} is not supported. Exp
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/b28d6ac67d6a38ec.
Report an issue: GitHub.