microsoft/qlib · error · ValueError

fields cannot be empty

Error message

fields cannot be empty

What it means

`Inst.get_column_names` requires at least one field. `D.features(...)` and the static data loaders build column names from the `fields` argument; an empty list means no data could ever be requested, so a ValueError is raised immediately.

Source

Thrown at qlib/data/data.py:538

                instruments_d = Inst.list_instruments(instruments=instruments, freq=freq, as_list=False)
            else:
                # dict of instruments and timestamp
                instruments_d = instruments
        elif isinstance(instruments, (list, tuple, pd.Index, np.ndarray)):
            # list or tuple of a group of instruments
            instruments_d = list(instruments)
        else:
            raise ValueError("Unsupported input type for param `instrument`")
        return instruments_d

    @staticmethod
    def get_column_names(fields):
        """
        Get column names from input fields

        """
        if len(fields) == 0:
            raise ValueError("fields cannot be empty")
        column_names = [str(f) for f in fields]
        return column_names

    @staticmethod
    def parse_fields(fields):
        # parse and check the input fields
        return [ExpressionD.get_expression_instance(f) for f in fields]

    @staticmethod
    def dataset_processor(instruments_d, column_names, start_time, end_time, freq, inst_processors=[]):
        """
        Load and process the data, return the data set.
        - default using multi-kernel method.

        """
        normalize_column_names = normalize_cache_fields(column_names)
        # One process for one task, so that the memory will be freed quicker.
        workers = max(min(C.get_kernels(freq), len(instruments_d)), 1)

View on GitHub (pinned to 79633dd950)

Solutions

  1. Pass at least one valid expression, e.g. `fields=['$close']`.
  2. Debug why your field list is empty: print len(fields) before the call.
  3. If fields are generated, add an early assert/guard in your config code.

Example fix

# before
df = D.features(insts, fields=[], start_time='2020-01-01', end_time='2020-12-31')

# after
df = D.features(insts, fields=['$close', '$volume'], start_time='2020-01-01', end_time='2020-12-31')
Defensive patterns

Strategy: validation

Validate before calling

assert isinstance(fields, (list, tuple)) and len(fields) > 0, 'fields must be a non-empty list of expressions'

Type guard

def has_fields(fields) -> bool:
    return bool(fields) and all(isinstance(f, str) for f in fields)

Prevention

When it happens

Trigger: Calling `D.features(instruments, fields=[])`, or building a DatasetH/DataHandler whose `data_loader` config has `fields: []`.

Common situations: Configs where fields are generated dynamically (e.g. alpha158 factors list comes back empty after a bug, or a YAML list is left as a placeholder). Also after renaming, when a fields-building helper returns [].

Related errors


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